mirror of
https://github.com/krahets/hello-algo.git
synced 2026-08-11 19:30:59 +00:00
Re-translate the Japanese version (#1871)
* Retranslate Japanese docs with GPT-5.4 * Retranslate Japanese code with GPT-5.4
This commit is contained in:
@@ -0,0 +1,130 @@
|
||||
# Created by https://www.toptal.com/developers/gitignore/api/objective-c,swift,swiftpackagemanager
|
||||
# Edit at https://www.toptal.com/developers/gitignore?templates=objective-c,swift,swiftpackagemanager
|
||||
|
||||
### Objective-C ###
|
||||
# Xcode
|
||||
#
|
||||
# gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore
|
||||
|
||||
## User settings
|
||||
xcuserdata/
|
||||
|
||||
## compatibility with Xcode 8 and earlier (ignoring not required starting Xcode 9)
|
||||
*.xcscmblueprint
|
||||
*.xccheckout
|
||||
|
||||
## compatibility with Xcode 3 and earlier (ignoring not required starting Xcode 4)
|
||||
build/
|
||||
DerivedData/
|
||||
*.moved-aside
|
||||
*.pbxuser
|
||||
!default.pbxuser
|
||||
*.mode1v3
|
||||
!default.mode1v3
|
||||
*.mode2v3
|
||||
!default.mode2v3
|
||||
*.perspectivev3
|
||||
!default.perspectivev3
|
||||
|
||||
## Obj-C/Swift specific
|
||||
*.hmap
|
||||
|
||||
## App packaging
|
||||
*.ipa
|
||||
*.dSYM.zip
|
||||
*.dSYM
|
||||
|
||||
# CocoaPods
|
||||
# We recommend against adding the Pods directory to your .gitignore. However
|
||||
# you should judge for yourself, the pros and cons are mentioned at:
|
||||
# https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control
|
||||
# Pods/
|
||||
# Add this line if you want to avoid checking in source code from the Xcode workspace
|
||||
# *.xcworkspace
|
||||
|
||||
# Carthage
|
||||
# Add this line if you want to avoid checking in source code from Carthage dependencies.
|
||||
# Carthage/Checkouts
|
||||
|
||||
Carthage/Build/
|
||||
|
||||
# fastlane
|
||||
# It is recommended to not store the screenshots in the git repo.
|
||||
# Instead, use fastlane to re-generate the screenshots whenever they are needed.
|
||||
# For more information about the recommended setup visit:
|
||||
# https://docs.fastlane.tools/best-practices/source-control/#source-control
|
||||
|
||||
fastlane/report.xml
|
||||
fastlane/Preview.html
|
||||
fastlane/screenshots/**/*.png
|
||||
fastlane/test_output
|
||||
|
||||
# Code Injection
|
||||
# After new code Injection tools there's a generated folder /iOSInjectionProject
|
||||
# https://github.com/johnno1962/injectionforxcode
|
||||
|
||||
iOSInjectionProject/
|
||||
|
||||
### Objective-C Patch ###
|
||||
|
||||
### Swift ###
|
||||
# Xcode
|
||||
# gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## Playgrounds
|
||||
timeline.xctimeline
|
||||
playground.xcworkspace
|
||||
|
||||
# Swift Package Manager
|
||||
# Add this line if you want to avoid checking in source code from Swift Package Manager dependencies.
|
||||
# Packages/
|
||||
# Package.pins
|
||||
# Package.resolved
|
||||
# *.xcodeproj
|
||||
# Xcode automatically generates this directory with a .xcworkspacedata file and xcuserdata
|
||||
# hence it is not needed unless you have added a package configuration file to your project
|
||||
# .swiftpm
|
||||
|
||||
.build/
|
||||
|
||||
# CocoaPods
|
||||
# We recommend against adding the Pods directory to your .gitignore. However
|
||||
# you should judge for yourself, the pros and cons are mentioned at:
|
||||
# https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control
|
||||
# Pods/
|
||||
# Add this line if you want to avoid checking in source code from the Xcode workspace
|
||||
# *.xcworkspace
|
||||
|
||||
# Carthage
|
||||
# Add this line if you want to avoid checking in source code from Carthage dependencies.
|
||||
# Carthage/Checkouts
|
||||
|
||||
|
||||
# Accio dependency management
|
||||
Dependencies/
|
||||
.accio/
|
||||
|
||||
# fastlane
|
||||
# It is recommended to not store the screenshots in the git repo.
|
||||
# Instead, use fastlane to re-generate the screenshots whenever they are needed.
|
||||
# For more information about the recommended setup visit:
|
||||
# https://docs.fastlane.tools/best-practices/source-control/#source-control
|
||||
|
||||
|
||||
# Code Injection
|
||||
# After new code Injection tools there's a generated folder /iOSInjectionProject
|
||||
# https://github.com/johnno1962/injectionforxcode
|
||||
|
||||
|
||||
### SwiftPackageManager ###
|
||||
Packages
|
||||
xcuserdata
|
||||
*.xcodeproj
|
||||
|
||||
|
||||
# End of https://www.toptal.com/developers/gitignore/api/objective-c,swift,swiftpackagemanager
|
||||
@@ -0,0 +1,14 @@
|
||||
{
|
||||
"pins" : [
|
||||
{
|
||||
"identity" : "swift-collections",
|
||||
"kind" : "remoteSourceControl",
|
||||
"location" : "https://github.com/apple/swift-collections",
|
||||
"state" : {
|
||||
"branch" : "release/1.1",
|
||||
"revision" : "4a1d92ba85027010d2c528c05576cde9a362254b"
|
||||
}
|
||||
}
|
||||
],
|
||||
"version" : 2
|
||||
}
|
||||
@@ -0,0 +1,206 @@
|
||||
// swift-tools-version: 5.7
|
||||
|
||||
import PackageDescription
|
||||
|
||||
let package = Package(
|
||||
name: "HelloAlgo",
|
||||
products: [
|
||||
// chapter_computational_complexity
|
||||
.executable(name: "iteration", targets: ["iteration"]),
|
||||
.executable(name: "recursion", targets: ["recursion"]),
|
||||
.executable(name: "time_complexity", targets: ["time_complexity"]),
|
||||
.executable(name: "worst_best_time_complexity", targets: ["worst_best_time_complexity"]),
|
||||
.executable(name: "space_complexity", targets: ["space_complexity"]),
|
||||
// chapter_array_and_linkedlist
|
||||
.executable(name: "array", targets: ["array"]),
|
||||
.executable(name: "linked_list", targets: ["linked_list"]),
|
||||
.executable(name: "list", targets: ["list"]),
|
||||
.executable(name: "my_list", targets: ["my_list"]),
|
||||
// chapter_stack_and_queue
|
||||
.executable(name: "stack", targets: ["stack"]),
|
||||
.executable(name: "linkedlist_stack", targets: ["linkedlist_stack"]),
|
||||
.executable(name: "array_stack", targets: ["array_stack"]),
|
||||
.executable(name: "queue", targets: ["queue"]),
|
||||
.executable(name: "linkedlist_queue", targets: ["linkedlist_queue"]),
|
||||
.executable(name: "array_queue", targets: ["array_queue"]),
|
||||
.executable(name: "deque", targets: ["deque"]),
|
||||
.executable(name: "linkedlist_deque", targets: ["linkedlist_deque"]),
|
||||
.executable(name: "array_deque", targets: ["array_deque"]),
|
||||
// chapter_hashing
|
||||
.executable(name: "hash_map", targets: ["hash_map"]),
|
||||
.executable(name: "array_hash_map", targets: ["array_hash_map"]),
|
||||
.executable(name: "hash_map_chaining", targets: ["hash_map_chaining"]),
|
||||
.executable(name: "hash_map_open_addressing", targets: ["hash_map_open_addressing"]),
|
||||
.executable(name: "simple_hash", targets: ["simple_hash"]),
|
||||
.executable(name: "built_in_hash", targets: ["built_in_hash"]),
|
||||
// chapter_tree
|
||||
.executable(name: "binary_tree", targets: ["binary_tree"]),
|
||||
.executable(name: "binary_tree_bfs", targets: ["binary_tree_bfs"]),
|
||||
.executable(name: "binary_tree_dfs", targets: ["binary_tree_dfs"]),
|
||||
.executable(name: "array_binary_tree", targets: ["array_binary_tree"]),
|
||||
.executable(name: "binary_search_tree", targets: ["binary_search_tree"]),
|
||||
.executable(name: "avl_tree", targets: ["avl_tree"]),
|
||||
// chapter_heap
|
||||
.executable(name: "heap", targets: ["heap"]),
|
||||
.executable(name: "my_heap", targets: ["my_heap"]),
|
||||
.executable(name: "top_k", targets: ["top_k"]),
|
||||
// chapter_graph
|
||||
.executable(name: "graph_adjacency_matrix", targets: ["graph_adjacency_matrix"]),
|
||||
.executable(name: "graph_adjacency_list", targets: ["graph_adjacency_list"]),
|
||||
.executable(name: "graph_bfs", targets: ["graph_bfs"]),
|
||||
.executable(name: "graph_dfs", targets: ["graph_dfs"]),
|
||||
// chapter_searching
|
||||
.executable(name: "binary_search", targets: ["binary_search"]),
|
||||
.executable(name: "binary_search_insertion", targets: ["binary_search_insertion"]),
|
||||
.executable(name: "binary_search_edge", targets: ["binary_search_edge"]),
|
||||
.executable(name: "two_sum", targets: ["two_sum"]),
|
||||
.executable(name: "linear_search", targets: ["linear_search"]),
|
||||
.executable(name: "hashing_search", targets: ["hashing_search"]),
|
||||
// chapter_sorting
|
||||
.executable(name: "selection_sort", targets: ["selection_sort"]),
|
||||
.executable(name: "bubble_sort", targets: ["bubble_sort"]),
|
||||
.executable(name: "insertion_sort", targets: ["insertion_sort"]),
|
||||
.executable(name: "quick_sort", targets: ["quick_sort"]),
|
||||
.executable(name: "merge_sort", targets: ["merge_sort"]),
|
||||
.executable(name: "heap_sort", targets: ["heap_sort"]),
|
||||
.executable(name: "bucket_sort", targets: ["bucket_sort"]),
|
||||
.executable(name: "counting_sort", targets: ["counting_sort"]),
|
||||
.executable(name: "radix_sort", targets: ["radix_sort"]),
|
||||
// chapter_divide_and_conquer
|
||||
.executable(name: "binary_search_recur", targets: ["binary_search_recur"]),
|
||||
.executable(name: "build_tree", targets: ["build_tree"]),
|
||||
.executable(name: "hanota", targets: ["hanota"]),
|
||||
// chapter_backtracking
|
||||
.executable(name: "preorder_traversal_i_compact", targets: ["preorder_traversal_i_compact"]),
|
||||
.executable(name: "preorder_traversal_ii_compact", targets: ["preorder_traversal_ii_compact"]),
|
||||
.executable(name: "preorder_traversal_iii_compact", targets: ["preorder_traversal_iii_compact"]),
|
||||
.executable(name: "preorder_traversal_iii_template", targets: ["preorder_traversal_iii_template"]),
|
||||
.executable(name: "permutations_i", targets: ["permutations_i"]),
|
||||
.executable(name: "permutations_ii", targets: ["permutations_ii"]),
|
||||
.executable(name: "subset_sum_i_naive", targets: ["subset_sum_i_naive"]),
|
||||
.executable(name: "subset_sum_i", targets: ["subset_sum_i"]),
|
||||
.executable(name: "subset_sum_ii", targets: ["subset_sum_ii"]),
|
||||
.executable(name: "n_queens", targets: ["n_queens"]),
|
||||
// chapter_dynamic_programming
|
||||
.executable(name: "climbing_stairs_backtrack", targets: ["climbing_stairs_backtrack"]),
|
||||
.executable(name: "climbing_stairs_dfs", targets: ["climbing_stairs_dfs"]),
|
||||
.executable(name: "climbing_stairs_dfs_mem", targets: ["climbing_stairs_dfs_mem"]),
|
||||
.executable(name: "climbing_stairs_dp", targets: ["climbing_stairs_dp"]),
|
||||
.executable(name: "min_cost_climbing_stairs_dp", targets: ["min_cost_climbing_stairs_dp"]),
|
||||
.executable(name: "climbing_stairs_constraint_dp", targets: ["climbing_stairs_constraint_dp"]),
|
||||
.executable(name: "min_path_sum", targets: ["min_path_sum"]),
|
||||
.executable(name: "knapsack", targets: ["knapsack"]),
|
||||
.executable(name: "unbounded_knapsack", targets: ["unbounded_knapsack"]),
|
||||
.executable(name: "coin_change", targets: ["coin_change"]),
|
||||
.executable(name: "coin_change_ii", targets: ["coin_change_ii"]),
|
||||
.executable(name: "edit_distance", targets: ["edit_distance"]),
|
||||
// chapter_greedy
|
||||
.executable(name: "coin_change_greedy", targets: ["coin_change_greedy"]),
|
||||
.executable(name: "fractional_knapsack", targets: ["fractional_knapsack"]),
|
||||
.executable(name: "max_capacity", targets: ["max_capacity"]),
|
||||
.executable(name: "max_product_cutting", targets: ["max_product_cutting"]),
|
||||
],
|
||||
dependencies: [
|
||||
.package(url: "https://github.com/apple/swift-collections", branch: "release/1.1"),
|
||||
],
|
||||
targets: [
|
||||
// helper
|
||||
.target(name: "utils", path: "utils"),
|
||||
.target(name: "graph_adjacency_list_target", dependencies: ["utils"], path: "chapter_graph", sources: ["graph_adjacency_list_target.swift"], swiftSettings: [.define("TARGET")]),
|
||||
.target(name: "binary_search_insertion_target", path: "chapter_searching", sources: ["binary_search_insertion_target.swift"], swiftSettings: [.define("TARGET")]),
|
||||
// chapter_computational_complexity
|
||||
.executableTarget(name: "iteration", path: "chapter_computational_complexity", sources: ["iteration.swift"]),
|
||||
.executableTarget(name: "recursion", path: "chapter_computational_complexity", sources: ["recursion.swift"]),
|
||||
.executableTarget(name: "time_complexity", path: "chapter_computational_complexity", sources: ["time_complexity.swift"]),
|
||||
.executableTarget(name: "worst_best_time_complexity", path: "chapter_computational_complexity", sources: ["worst_best_time_complexity.swift"]),
|
||||
.executableTarget(name: "space_complexity", dependencies: ["utils"], path: "chapter_computational_complexity", sources: ["space_complexity.swift"]),
|
||||
// chapter_array_and_linkedlist
|
||||
.executableTarget(name: "array", path: "chapter_array_and_linkedlist", sources: ["array.swift"]),
|
||||
.executableTarget(name: "linked_list", dependencies: ["utils"], path: "chapter_array_and_linkedlist", sources: ["linked_list.swift"]),
|
||||
.executableTarget(name: "list", path: "chapter_array_and_linkedlist", sources: ["list.swift"]),
|
||||
.executableTarget(name: "my_list", path: "chapter_array_and_linkedlist", sources: ["my_list.swift"]),
|
||||
// chapter_stack_and_queue
|
||||
.executableTarget(name: "stack", path: "chapter_stack_and_queue", sources: ["stack.swift"]),
|
||||
.executableTarget(name: "linkedlist_stack", dependencies: ["utils"], path: "chapter_stack_and_queue", sources: ["linkedlist_stack.swift"]),
|
||||
.executableTarget(name: "array_stack", path: "chapter_stack_and_queue", sources: ["array_stack.swift"]),
|
||||
.executableTarget(name: "queue", path: "chapter_stack_and_queue", sources: ["queue.swift"]),
|
||||
.executableTarget(name: "linkedlist_queue", dependencies: ["utils"], path: "chapter_stack_and_queue", sources: ["linkedlist_queue.swift"]),
|
||||
.executableTarget(name: "array_queue", path: "chapter_stack_and_queue", sources: ["array_queue.swift"]),
|
||||
.executableTarget(name: "deque", path: "chapter_stack_and_queue", sources: ["deque.swift"]),
|
||||
.executableTarget(name: "linkedlist_deque", path: "chapter_stack_and_queue", sources: ["linkedlist_deque.swift"]),
|
||||
.executableTarget(name: "array_deque", path: "chapter_stack_and_queue", sources: ["array_deque.swift"]),
|
||||
// chapter_hashing
|
||||
.executableTarget(name: "hash_map", dependencies: ["utils"], path: "chapter_hashing", sources: ["hash_map.swift"]),
|
||||
.executableTarget(name: "array_hash_map", dependencies: ["utils"], path: "chapter_hashing", sources: ["array_hash_map.swift"]),
|
||||
.executableTarget(name: "hash_map_chaining", dependencies: ["utils"], path: "chapter_hashing", sources: ["hash_map_chaining.swift"]),
|
||||
.executableTarget(name: "hash_map_open_addressing", dependencies: ["utils"], path: "chapter_hashing", sources: ["hash_map_open_addressing.swift"]),
|
||||
.executableTarget(name: "simple_hash", path: "chapter_hashing", sources: ["simple_hash.swift"]),
|
||||
.executableTarget(name: "built_in_hash", dependencies: ["utils"], path: "chapter_hashing", sources: ["built_in_hash.swift"]),
|
||||
// chapter_tree
|
||||
.executableTarget(name: "binary_tree", dependencies: ["utils"], path: "chapter_tree", sources: ["binary_tree.swift"]),
|
||||
.executableTarget(name: "binary_tree_bfs", dependencies: ["utils"], path: "chapter_tree", sources: ["binary_tree_bfs.swift"]),
|
||||
.executableTarget(name: "binary_tree_dfs", dependencies: ["utils"], path: "chapter_tree", sources: ["binary_tree_dfs.swift"]),
|
||||
.executableTarget(name: "array_binary_tree", dependencies: ["utils"], path: "chapter_tree", sources: ["array_binary_tree.swift"]),
|
||||
.executableTarget(name: "binary_search_tree", dependencies: ["utils"], path: "chapter_tree", sources: ["binary_search_tree.swift"]),
|
||||
.executableTarget(name: "avl_tree", dependencies: ["utils"], path: "chapter_tree", sources: ["avl_tree.swift"]),
|
||||
// chapter_heap
|
||||
.executableTarget(name: "heap", dependencies: ["utils", .product(name: "HeapModule", package: "swift-collections")], path: "chapter_heap", sources: ["heap.swift"]),
|
||||
.executableTarget(name: "my_heap", dependencies: ["utils"], path: "chapter_heap", sources: ["my_heap.swift"]),
|
||||
.executableTarget(name: "top_k", dependencies: ["utils", .product(name: "HeapModule", package: "swift-collections")], path: "chapter_heap", sources: ["top_k.swift"]),
|
||||
// chapter_graph
|
||||
.executableTarget(name: "graph_adjacency_matrix", dependencies: ["utils"], path: "chapter_graph", sources: ["graph_adjacency_matrix.swift"]),
|
||||
.executableTarget(name: "graph_adjacency_list", dependencies: ["utils"], path: "chapter_graph", sources: ["graph_adjacency_list.swift"]),
|
||||
.executableTarget(name: "graph_bfs", dependencies: ["utils", "graph_adjacency_list_target"], path: "chapter_graph", sources: ["graph_bfs.swift"]),
|
||||
.executableTarget(name: "graph_dfs", dependencies: ["utils", "graph_adjacency_list_target"], path: "chapter_graph", sources: ["graph_dfs.swift"]),
|
||||
// chapter_searching
|
||||
.executableTarget(name: "binary_search", path: "chapter_searching", sources: ["binary_search.swift"]),
|
||||
.executableTarget(name: "binary_search_insertion", path: "chapter_searching", sources: ["binary_search_insertion.swift"]),
|
||||
.executableTarget(name: "binary_search_edge", dependencies: ["binary_search_insertion_target"], path: "chapter_searching", sources: ["binary_search_edge.swift"]),
|
||||
.executableTarget(name: "two_sum", path: "chapter_searching", sources: ["two_sum.swift"]),
|
||||
.executableTarget(name: "linear_search", dependencies: ["utils"], path: "chapter_searching", sources: ["linear_search.swift"]),
|
||||
.executableTarget(name: "hashing_search", dependencies: ["utils"], path: "chapter_searching", sources: ["hashing_search.swift"]),
|
||||
// chapter_sorting
|
||||
.executableTarget(name: "selection_sort", path: "chapter_sorting", sources: ["selection_sort.swift"]),
|
||||
.executableTarget(name: "bubble_sort", path: "chapter_sorting", sources: ["bubble_sort.swift"]),
|
||||
.executableTarget(name: "insertion_sort", path: "chapter_sorting", sources: ["insertion_sort.swift"]),
|
||||
.executableTarget(name: "quick_sort", path: "chapter_sorting", sources: ["quick_sort.swift"]),
|
||||
.executableTarget(name: "merge_sort", path: "chapter_sorting", sources: ["merge_sort.swift"]),
|
||||
.executableTarget(name: "heap_sort", path: "chapter_sorting", sources: ["heap_sort.swift"]),
|
||||
.executableTarget(name: "bucket_sort", path: "chapter_sorting", sources: ["bucket_sort.swift"]),
|
||||
.executableTarget(name: "counting_sort", path: "chapter_sorting", sources: ["counting_sort.swift"]),
|
||||
.executableTarget(name: "radix_sort", path: "chapter_sorting", sources: ["radix_sort.swift"]),
|
||||
// chapter_divide_and_conquer
|
||||
.executableTarget(name: "binary_search_recur", path: "chapter_divide_and_conquer", sources: ["binary_search_recur.swift"]),
|
||||
.executableTarget(name: "build_tree", dependencies: ["utils"], path: "chapter_divide_and_conquer", sources: ["build_tree.swift"]),
|
||||
.executableTarget(name: "hanota", path: "chapter_divide_and_conquer", sources: ["hanota.swift"]),
|
||||
// chapter_backtracking
|
||||
.executableTarget(name: "preorder_traversal_i_compact", dependencies: ["utils"], path: "chapter_backtracking", sources: ["preorder_traversal_i_compact.swift"]),
|
||||
.executableTarget(name: "preorder_traversal_ii_compact", dependencies: ["utils"], path: "chapter_backtracking", sources: ["preorder_traversal_ii_compact.swift"]),
|
||||
.executableTarget(name: "preorder_traversal_iii_compact", dependencies: ["utils"], path: "chapter_backtracking", sources: ["preorder_traversal_iii_compact.swift"]),
|
||||
.executableTarget(name: "preorder_traversal_iii_template", dependencies: ["utils"], path: "chapter_backtracking", sources: ["preorder_traversal_iii_template.swift"]),
|
||||
.executableTarget(name: "permutations_i", path: "chapter_backtracking", sources: ["permutations_i.swift"]),
|
||||
.executableTarget(name: "permutations_ii", path: "chapter_backtracking", sources: ["permutations_ii.swift"]),
|
||||
.executableTarget(name: "subset_sum_i_naive", path: "chapter_backtracking", sources: ["subset_sum_i_naive.swift"]),
|
||||
.executableTarget(name: "subset_sum_i", path: "chapter_backtracking", sources: ["subset_sum_i.swift"]),
|
||||
.executableTarget(name: "subset_sum_ii", path: "chapter_backtracking", sources: ["subset_sum_ii.swift"]),
|
||||
.executableTarget(name: "n_queens", path: "chapter_backtracking", sources: ["n_queens.swift"]),
|
||||
// chapter_dynamic_programming
|
||||
.executableTarget(name: "climbing_stairs_backtrack", path: "chapter_dynamic_programming", sources: ["climbing_stairs_backtrack.swift"]),
|
||||
.executableTarget(name: "climbing_stairs_dfs", path: "chapter_dynamic_programming", sources: ["climbing_stairs_dfs.swift"]),
|
||||
.executableTarget(name: "climbing_stairs_dfs_mem", path: "chapter_dynamic_programming", sources: ["climbing_stairs_dfs_mem.swift"]),
|
||||
.executableTarget(name: "climbing_stairs_dp", path: "chapter_dynamic_programming", sources: ["climbing_stairs_dp.swift"]),
|
||||
.executableTarget(name: "min_cost_climbing_stairs_dp", path: "chapter_dynamic_programming", sources: ["min_cost_climbing_stairs_dp.swift"]),
|
||||
.executableTarget(name: "climbing_stairs_constraint_dp", path: "chapter_dynamic_programming", sources: ["climbing_stairs_constraint_dp.swift"]),
|
||||
.executableTarget(name: "min_path_sum", path: "chapter_dynamic_programming", sources: ["min_path_sum.swift"]),
|
||||
.executableTarget(name: "knapsack", path: "chapter_dynamic_programming", sources: ["knapsack.swift"]),
|
||||
.executableTarget(name: "unbounded_knapsack", path: "chapter_dynamic_programming", sources: ["unbounded_knapsack.swift"]),
|
||||
.executableTarget(name: "coin_change", path: "chapter_dynamic_programming", sources: ["coin_change.swift"]),
|
||||
.executableTarget(name: "coin_change_ii", path: "chapter_dynamic_programming", sources: ["coin_change_ii.swift"]),
|
||||
.executableTarget(name: "edit_distance", path: "chapter_dynamic_programming", sources: ["edit_distance.swift"]),
|
||||
// chapter_greedy
|
||||
.executableTarget(name: "coin_change_greedy", path: "chapter_greedy", sources: ["coin_change_greedy.swift"]),
|
||||
.executableTarget(name: "fractional_knapsack", path: "chapter_greedy", sources: ["fractional_knapsack.swift"]),
|
||||
.executableTarget(name: "max_capacity", path: "chapter_greedy", sources: ["max_capacity.swift"]),
|
||||
.executableTarget(name: "max_product_cutting", path: "chapter_greedy", sources: ["max_product_cutting.swift"]),
|
||||
]
|
||||
)
|
||||
@@ -0,0 +1,107 @@
|
||||
/**
|
||||
* File: array.swift
|
||||
* Created Time: 2023-01-05
|
||||
* Author: nuomi1 (nuomi1@qq.com)
|
||||
*/
|
||||
|
||||
/* 要素へランダムアクセス */
|
||||
func randomAccess(nums: [Int]) -> Int {
|
||||
// 区間 [0, nums.count) からランダムに数字を 1 つ選ぶ
|
||||
let randomIndex = nums.indices.randomElement()!
|
||||
// ランダムな要素を取得して返す
|
||||
let randomNum = nums[randomIndex]
|
||||
return randomNum
|
||||
}
|
||||
|
||||
/* 配列長を拡張する */
|
||||
func extend(nums: [Int], enlarge: Int) -> [Int] {
|
||||
// 拡張後の長さを持つ配列を初期化する
|
||||
var res = Array(repeating: 0, count: nums.count + enlarge)
|
||||
// 元の配列の全要素を新しい配列にコピー
|
||||
for i in nums.indices {
|
||||
res[i] = nums[i]
|
||||
}
|
||||
// 拡張後の新しい配列を返す
|
||||
return res
|
||||
}
|
||||
|
||||
/* 配列の index 番目に要素 num を挿入 */
|
||||
func insert(nums: inout [Int], num: Int, index: Int) {
|
||||
// インデックス index 以降の全要素を 1 つ後ろへ移動する
|
||||
for i in nums.indices.dropFirst(index).reversed() {
|
||||
nums[i] = nums[i - 1]
|
||||
}
|
||||
// index の要素に num を代入する
|
||||
nums[index] = num
|
||||
}
|
||||
|
||||
/* index の要素を削除する */
|
||||
func remove(nums: inout [Int], index: Int) {
|
||||
// インデックス index より後ろの全要素を 1 つ前へ移動する
|
||||
for i in nums.indices.dropFirst(index).dropLast() {
|
||||
nums[i] = nums[i + 1]
|
||||
}
|
||||
}
|
||||
|
||||
/* 配列を走査 */
|
||||
func traverse(nums: [Int]) {
|
||||
var count = 0
|
||||
// インデックスで配列を走査
|
||||
for i in nums.indices {
|
||||
count += nums[i]
|
||||
}
|
||||
// 配列要素を直接走査
|
||||
for num in nums {
|
||||
count += num
|
||||
}
|
||||
// データのインデックスと要素を同時に走査する
|
||||
for (i, num) in nums.enumerated() {
|
||||
count += nums[i]
|
||||
count += num
|
||||
}
|
||||
}
|
||||
|
||||
/* 配列内で指定要素を探す */
|
||||
func find(nums: [Int], target: Int) -> Int {
|
||||
for i in nums.indices {
|
||||
if nums[i] == target {
|
||||
return i
|
||||
}
|
||||
}
|
||||
return -1
|
||||
}
|
||||
|
||||
@main
|
||||
enum _Array {
|
||||
/* Driver Code */
|
||||
static func main() {
|
||||
/* 配列を初期化 */
|
||||
let arr = Array(repeating: 0, count: 5)
|
||||
print("配列 arr = \(arr)")
|
||||
var nums = [1, 3, 2, 5, 4]
|
||||
print("配列 nums = \(nums)")
|
||||
|
||||
/* ランダムアクセス */
|
||||
let randomNum = randomAccess(nums: nums)
|
||||
print("nums からランダムな要素を取得 \(randomNum)")
|
||||
|
||||
/* 長さを拡張 */
|
||||
nums = extend(nums: nums, enlarge: 3)
|
||||
print("配列の長さを 8 に拡張すると、nums = \(nums)")
|
||||
|
||||
/* 要素を挿入する */
|
||||
insert(nums: &nums, num: 6, index: 3)
|
||||
print("インデックス 3 に数値 6 を挿入すると、nums = \(nums)")
|
||||
|
||||
/* 要素を削除 */
|
||||
remove(nums: &nums, index: 2)
|
||||
print("インデックス 2 の要素を削除すると、nums = \(nums)")
|
||||
|
||||
/* 配列を走査 */
|
||||
traverse(nums: nums)
|
||||
|
||||
/* 要素を探索する */
|
||||
let index = find(nums: nums, target: 3)
|
||||
print("nums 内で要素 3 を検索すると、インデックス = \(index)")
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,90 @@
|
||||
/**
|
||||
* File: linked_list.swift
|
||||
* Created Time: 2023-01-08
|
||||
* Author: nuomi1 (nuomi1@qq.com)
|
||||
*/
|
||||
|
||||
import utils
|
||||
|
||||
/* 連結リストでノード n0 の後ろにノード P を挿入する */
|
||||
func insert(n0: ListNode, P: ListNode) {
|
||||
let n1 = n0.next
|
||||
P.next = n1
|
||||
n0.next = P
|
||||
}
|
||||
|
||||
/* 連結リストでノード n0 の直後のノードを削除する */
|
||||
func remove(n0: ListNode) {
|
||||
if n0.next == nil {
|
||||
return
|
||||
}
|
||||
// n0 -> P -> n1
|
||||
let P = n0.next
|
||||
let n1 = P?.next
|
||||
n0.next = n1
|
||||
}
|
||||
|
||||
/* 連結リスト内で index 番目のノードにアクセス */
|
||||
func access(head: ListNode, index: Int) -> ListNode? {
|
||||
var head: ListNode? = head
|
||||
for _ in 0 ..< index {
|
||||
if head == nil {
|
||||
return nil
|
||||
}
|
||||
head = head?.next
|
||||
}
|
||||
return head
|
||||
}
|
||||
|
||||
/* 連結リストで値が target の最初のノードを探す */
|
||||
func find(head: ListNode, target: Int) -> Int {
|
||||
var head: ListNode? = head
|
||||
var index = 0
|
||||
while head != nil {
|
||||
if head?.val == target {
|
||||
return index
|
||||
}
|
||||
head = head?.next
|
||||
index += 1
|
||||
}
|
||||
return -1
|
||||
}
|
||||
|
||||
@main
|
||||
enum LinkedList {
|
||||
/* Driver Code */
|
||||
static func main() {
|
||||
/* 連結リストを初期化 */
|
||||
// 各ノードを初期化
|
||||
let n0 = ListNode(x: 1)
|
||||
let n1 = ListNode(x: 3)
|
||||
let n2 = ListNode(x: 2)
|
||||
let n3 = ListNode(x: 5)
|
||||
let n4 = ListNode(x: 4)
|
||||
// ノード間の参照を構築する
|
||||
n0.next = n1
|
||||
n1.next = n2
|
||||
n2.next = n3
|
||||
n3.next = n4
|
||||
print("初期化した連結リストは")
|
||||
PrintUtil.printLinkedList(head: n0)
|
||||
|
||||
/* ノードを挿入 */
|
||||
insert(n0: n0, P: ListNode(x: 0))
|
||||
print("ノード挿入後の連結リストは")
|
||||
PrintUtil.printLinkedList(head: n0)
|
||||
|
||||
/* ノードを削除 */
|
||||
remove(n0: n0)
|
||||
print("ノード削除後の連結リストは")
|
||||
PrintUtil.printLinkedList(head: n0)
|
||||
|
||||
/* ノードにアクセス */
|
||||
let node = access(head: n0, index: 3)
|
||||
print("連結リストのインデックス 3 にあるノードの値 = \(node!.val)")
|
||||
|
||||
/* ノードを探索 */
|
||||
let index = find(head: n0, target: 2)
|
||||
print("連結リスト内で値が 2 のノードのインデックス = \(index)")
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
/**
|
||||
* File: list.swift
|
||||
* Created Time: 2023-01-08
|
||||
* Author: nuomi1 (nuomi1@qq.com)
|
||||
*/
|
||||
|
||||
@main
|
||||
enum List {
|
||||
/* Driver Code */
|
||||
static func main() {
|
||||
/* リストを初期化 */
|
||||
var nums = [1, 3, 2, 5, 4]
|
||||
print("リスト nums = \(nums)")
|
||||
|
||||
/* 要素にアクセス */
|
||||
let num = nums[1]
|
||||
print("インデックス 1 の要素にアクセスすると、num = \(num)")
|
||||
|
||||
/* 要素を更新 */
|
||||
nums[1] = 0
|
||||
print("インデックス 1 の要素を 0 に更新すると、nums = \(nums)")
|
||||
|
||||
/* リストを空にする */
|
||||
nums.removeAll()
|
||||
print("リストを空にした後、nums = \(nums)")
|
||||
|
||||
/* 末尾に要素を追加 */
|
||||
nums.append(1)
|
||||
nums.append(3)
|
||||
nums.append(2)
|
||||
nums.append(5)
|
||||
nums.append(4)
|
||||
print("要素追加後、nums = \(nums)")
|
||||
|
||||
/* 中間に要素を挿入 */
|
||||
nums.insert(6, at: 3)
|
||||
print("インデックス 3 に数値 6 を挿入すると、nums = \(nums)")
|
||||
|
||||
/* 要素を削除 */
|
||||
nums.remove(at: 3)
|
||||
print("インデックス 3 の要素を削除すると、nums = \(nums)")
|
||||
|
||||
/* インデックスでリストを走査 */
|
||||
var count = 0
|
||||
for i in nums.indices {
|
||||
count += nums[i]
|
||||
}
|
||||
/* リスト要素を直接走査 */
|
||||
count = 0
|
||||
for x in nums {
|
||||
count += x
|
||||
}
|
||||
|
||||
/* 2 つのリストを連結する */
|
||||
let nums1 = [6, 8, 7, 10, 9]
|
||||
nums.append(contentsOf: nums1)
|
||||
print("リスト nums1 を nums の後ろに連結すると、nums = \(nums)")
|
||||
|
||||
/* リストをソート */
|
||||
nums.sort()
|
||||
print("リストをソートした後、nums = \(nums)")
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,146 @@
|
||||
/**
|
||||
* File: my_list.swift
|
||||
* Created Time: 2023-01-08
|
||||
* Author: nuomi1 (nuomi1@qq.com)
|
||||
*/
|
||||
|
||||
/* リストクラス */
|
||||
class MyList {
|
||||
private var arr: [Int] // 配列(リスト要素を格納)
|
||||
private var _capacity: Int // リスト容量
|
||||
private var _size: Int // リストの長さ(現在の要素数)
|
||||
private let extendRatio: Int // リスト拡張時の増加倍率
|
||||
|
||||
/* コンストラクタ */
|
||||
init() {
|
||||
_capacity = 10
|
||||
_size = 0
|
||||
extendRatio = 2
|
||||
arr = Array(repeating: 0, count: _capacity)
|
||||
}
|
||||
|
||||
/* リストの長さを取得(現在の要素数) */
|
||||
func size() -> Int {
|
||||
_size
|
||||
}
|
||||
|
||||
/* リスト容量を取得する */
|
||||
func capacity() -> Int {
|
||||
_capacity
|
||||
}
|
||||
|
||||
/* 要素にアクセス */
|
||||
func get(index: Int) -> Int {
|
||||
// インデックスが範囲外ならエラーを投げる。以下同様
|
||||
if index < 0 || index >= size() {
|
||||
fatalError("インデックスが範囲外")
|
||||
}
|
||||
return arr[index]
|
||||
}
|
||||
|
||||
/* 要素を更新 */
|
||||
func set(index: Int, num: Int) {
|
||||
if index < 0 || index >= size() {
|
||||
fatalError("インデックスが範囲外")
|
||||
}
|
||||
arr[index] = num
|
||||
}
|
||||
|
||||
/* 末尾に要素を追加 */
|
||||
func add(num: Int) {
|
||||
// 要素数が容量を超えると、拡張機構が発動する
|
||||
if size() == capacity() {
|
||||
extendCapacity()
|
||||
}
|
||||
arr[size()] = num
|
||||
// 要素数を更新
|
||||
_size += 1
|
||||
}
|
||||
|
||||
/* 中間に要素を挿入 */
|
||||
func insert(index: Int, num: Int) {
|
||||
if index < 0 || index >= size() {
|
||||
fatalError("インデックスが範囲外")
|
||||
}
|
||||
// 要素数が容量を超えると、拡張機構が発動する
|
||||
if size() == capacity() {
|
||||
extendCapacity()
|
||||
}
|
||||
// index 以降の要素をすべて 1 つ後ろへずらす
|
||||
for j in (index ..< size()).reversed() {
|
||||
arr[j + 1] = arr[j]
|
||||
}
|
||||
arr[index] = num
|
||||
// 要素数を更新
|
||||
_size += 1
|
||||
}
|
||||
|
||||
/* 要素を削除 */
|
||||
@discardableResult
|
||||
func remove(index: Int) -> Int {
|
||||
if index < 0 || index >= size() {
|
||||
fatalError("インデックスが範囲外")
|
||||
}
|
||||
let num = arr[index]
|
||||
// インデックス index より後の要素をすべて 1 つ前に移動する
|
||||
for j in index ..< (size() - 1) {
|
||||
arr[j] = arr[j + 1]
|
||||
}
|
||||
// 要素数を更新
|
||||
_size -= 1
|
||||
// 削除された要素を返す
|
||||
return num
|
||||
}
|
||||
|
||||
/* リストの拡張 */
|
||||
func extendCapacity() {
|
||||
// 元の配列の extendRatio 倍の長さを持つ新しい配列を作成し、元の配列をコピーする
|
||||
arr = arr + Array(repeating: 0, count: capacity() * (extendRatio - 1))
|
||||
// リストの容量を更新
|
||||
_capacity = arr.count
|
||||
}
|
||||
|
||||
/* リストを配列に変換する */
|
||||
func toArray() -> [Int] {
|
||||
Array(arr.prefix(size()))
|
||||
}
|
||||
}
|
||||
|
||||
@main
|
||||
enum _MyList {
|
||||
/* Driver Code */
|
||||
static func main() {
|
||||
/* リストを初期化 */
|
||||
let nums = MyList()
|
||||
/* 末尾に要素を追加 */
|
||||
nums.add(num: 1)
|
||||
nums.add(num: 3)
|
||||
nums.add(num: 2)
|
||||
nums.add(num: 5)
|
||||
nums.add(num: 4)
|
||||
print("リスト nums = \(nums.toArray()) 、容量 = \(nums.capacity()) 、長さ = \(nums.size())")
|
||||
|
||||
/* 中間に要素を挿入 */
|
||||
nums.insert(index: 3, num: 6)
|
||||
print("インデックス 3 に数値 6 を挿入すると、nums = \(nums.toArray())")
|
||||
|
||||
/* 要素を削除 */
|
||||
nums.remove(index: 3)
|
||||
print("インデックス 3 の要素を削除すると、nums = \(nums.toArray())")
|
||||
|
||||
/* 要素にアクセス */
|
||||
let num = nums.get(index: 1)
|
||||
print("インデックス 1 の要素にアクセスすると、num = \(num)")
|
||||
|
||||
/* 要素を更新 */
|
||||
nums.set(index: 1, num: 0)
|
||||
print("インデックス 1 の要素を 0 に更新すると、nums = \(nums.toArray())")
|
||||
|
||||
/* 拡張機構をテストする */
|
||||
for i in 0 ..< 10 {
|
||||
// i = 5 のとき、リスト長が容量を超えるため、この時点で拡張機構が発動する
|
||||
nums.add(num: i)
|
||||
}
|
||||
print("拡張後のリスト nums = \(nums.toArray()) 、容量 = \(nums.capacity()) 、長さ = \(nums.size())")
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
/**
|
||||
* File: n_queens.swift
|
||||
* Created Time: 2023-05-14
|
||||
* Author: nuomi1 (nuomi1@qq.com)
|
||||
*/
|
||||
|
||||
/* バックトラッキング:N クイーン */
|
||||
func backtrack(row: Int, n: Int, state: inout [[String]], res: inout [[[String]]], cols: inout [Bool], diags1: inout [Bool], diags2: inout [Bool]) {
|
||||
// すべての行への配置が完了したら、解を記録する
|
||||
if row == n {
|
||||
res.append(state)
|
||||
return
|
||||
}
|
||||
// すべての列を走査
|
||||
for col in 0 ..< n {
|
||||
// このマスに対応する主対角線と副対角線を計算
|
||||
let diag1 = row - col + n - 1
|
||||
let diag2 = row + col
|
||||
// 枝刈り:そのマスの列、主対角線、副対角線にクイーンがあってはならない
|
||||
if !cols[col] && !diags1[diag1] && !diags2[diag2] {
|
||||
// 試行:そのマスにクイーンを置く
|
||||
state[row][col] = "Q"
|
||||
cols[col] = true
|
||||
diags1[diag1] = true
|
||||
diags2[diag2] = true
|
||||
// 次の行に配置する
|
||||
backtrack(row: row + 1, n: n, state: &state, res: &res, cols: &cols, diags1: &diags1, diags2: &diags2)
|
||||
// 戻す:そのマスを空きマスに戻す
|
||||
state[row][col] = "#"
|
||||
cols[col] = false
|
||||
diags1[diag1] = false
|
||||
diags2[diag2] = false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* N クイーンを解く */
|
||||
func nQueens(n: Int) -> [[[String]]] {
|
||||
// n*n の盤面を初期化する。'Q' はクイーン、'#' は空きマスを表す
|
||||
var state = Array(repeating: Array(repeating: "#", count: n), count: n)
|
||||
var cols = Array(repeating: false, count: n) // 列にクイーンがあるか記録
|
||||
var diags1 = Array(repeating: false, count: 2 * n - 1) // 主対角線にクイーンがあるかを記録
|
||||
var diags2 = Array(repeating: false, count: 2 * n - 1) // 副対角線にクイーンがあるかを記録
|
||||
var res: [[[String]]] = []
|
||||
|
||||
backtrack(row: 0, n: n, state: &state, res: &res, cols: &cols, diags1: &diags1, diags2: &diags2)
|
||||
|
||||
return res
|
||||
}
|
||||
|
||||
@main
|
||||
enum NQueens {
|
||||
/* Driver Code */
|
||||
static func main() {
|
||||
let n = 4
|
||||
let res = nQueens(n: n)
|
||||
|
||||
print("入力された盤面の縦横は \(n)")
|
||||
print("クイーンの配置方法は全部で \(res.count) 通り")
|
||||
for state in res {
|
||||
print("--------------------")
|
||||
for row in state {
|
||||
print(row)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
/**
|
||||
* File: permutations_i.swift
|
||||
* Created Time: 2023-04-30
|
||||
* Author: nuomi1 (nuomi1@qq.com)
|
||||
*/
|
||||
|
||||
/* バックトラッキング:順列 I */
|
||||
func backtrack(state: inout [Int], choices: [Int], selected: inout [Bool], res: inout [[Int]]) {
|
||||
// 状態の長さが要素数に等しければ、解を記録
|
||||
if state.count == choices.count {
|
||||
res.append(state)
|
||||
return
|
||||
}
|
||||
// すべての選択肢を走査
|
||||
for (i, choice) in choices.enumerated() {
|
||||
// 枝刈り:要素の重複選択を許可しない
|
||||
if !selected[i] {
|
||||
// 試行: 選択を行い、状態を更新
|
||||
selected[i] = true
|
||||
state.append(choice)
|
||||
// 次の選択へ進む
|
||||
backtrack(state: &state, choices: choices, selected: &selected, res: &res)
|
||||
// バックトラック:選択を取り消し、前の状態に戻す
|
||||
selected[i] = false
|
||||
state.removeLast()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* 全順列 I */
|
||||
func permutationsI(nums: [Int]) -> [[Int]] {
|
||||
var state: [Int] = []
|
||||
var selected = Array(repeating: false, count: nums.count)
|
||||
var res: [[Int]] = []
|
||||
backtrack(state: &state, choices: nums, selected: &selected, res: &res)
|
||||
return res
|
||||
}
|
||||
|
||||
@main
|
||||
enum PermutationsI {
|
||||
/* Driver Code */
|
||||
static func main() {
|
||||
let nums = [1, 2, 3]
|
||||
|
||||
let res = permutationsI(nums: nums)
|
||||
|
||||
print("入力配列 nums = \(nums)")
|
||||
print("すべての順列 res = \(res)")
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
/**
|
||||
* File: permutations_ii.swift
|
||||
* Created Time: 2023-04-30
|
||||
* Author: nuomi1 (nuomi1@qq.com)
|
||||
*/
|
||||
|
||||
/* バックトラッキング:順列 II */
|
||||
func backtrack(state: inout [Int], choices: [Int], selected: inout [Bool], res: inout [[Int]]) {
|
||||
// 状態の長さが要素数に等しければ、解を記録
|
||||
if state.count == choices.count {
|
||||
res.append(state)
|
||||
return
|
||||
}
|
||||
// すべての選択肢を走査
|
||||
var duplicated: Set<Int> = []
|
||||
for (i, choice) in choices.enumerated() {
|
||||
// 枝刈り:要素の重複選択を許可せず、同値要素の重複選択も許可しない
|
||||
if !selected[i], !duplicated.contains(choice) {
|
||||
// 試行: 選択を行い、状態を更新
|
||||
duplicated.insert(choice) // 選択済みの要素値を記録
|
||||
selected[i] = true
|
||||
state.append(choice)
|
||||
// 次の選択へ進む
|
||||
backtrack(state: &state, choices: choices, selected: &selected, res: &res)
|
||||
// バックトラック:選択を取り消し、前の状態に戻す
|
||||
selected[i] = false
|
||||
state.removeLast()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* 全順列 II */
|
||||
func permutationsII(nums: [Int]) -> [[Int]] {
|
||||
var state: [Int] = []
|
||||
var selected = Array(repeating: false, count: nums.count)
|
||||
var res: [[Int]] = []
|
||||
backtrack(state: &state, choices: nums, selected: &selected, res: &res)
|
||||
return res
|
||||
}
|
||||
|
||||
@main
|
||||
enum PermutationsII {
|
||||
/* Driver Code */
|
||||
static func main() {
|
||||
let nums = [1, 2, 3]
|
||||
|
||||
let res = permutationsII(nums: nums)
|
||||
|
||||
print("入力配列 nums = \(nums)")
|
||||
print("すべての順列 res = \(res)")
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
/**
|
||||
* File: preorder_traversal_i_compact.swift
|
||||
* Created Time: 2023-04-30
|
||||
* Author: nuomi1 (nuomi1@qq.com)
|
||||
*/
|
||||
|
||||
import utils
|
||||
|
||||
var res: [TreeNode] = []
|
||||
|
||||
/* 前順走査:例題 1 */
|
||||
func preOrder(root: TreeNode?) {
|
||||
guard let root = root else {
|
||||
return
|
||||
}
|
||||
if root.val == 7 {
|
||||
// 解を記録
|
||||
res.append(root)
|
||||
}
|
||||
preOrder(root: root.left)
|
||||
preOrder(root: root.right)
|
||||
}
|
||||
|
||||
@main
|
||||
enum PreorderTraversalICompact {
|
||||
/* Driver Code */
|
||||
static func main() {
|
||||
let root = TreeNode.listToTree(arr: [1, 7, 3, 4, 5, 6, 7])
|
||||
print("\n二分木を初期化")
|
||||
PrintUtil.printTree(root: root)
|
||||
|
||||
// 先行順走査
|
||||
res = []
|
||||
preOrder(root: root)
|
||||
|
||||
print("\n値が 7 のすべてのノードを出力")
|
||||
var vals: [Int] = []
|
||||
for node in res {
|
||||
vals.append(node.val)
|
||||
}
|
||||
print(vals)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
/**
|
||||
* File: preorder_traversal_ii_compact.swift
|
||||
* Created Time: 2023-04-30
|
||||
* Author: nuomi1 (nuomi1@qq.com)
|
||||
*/
|
||||
|
||||
import utils
|
||||
|
||||
var path: [TreeNode] = []
|
||||
var res: [[TreeNode]] = []
|
||||
|
||||
/* 前順走査:例題 2 */
|
||||
func preOrder(root: TreeNode?) {
|
||||
guard let root = root else {
|
||||
return
|
||||
}
|
||||
// 試す
|
||||
path.append(root)
|
||||
if root.val == 7 {
|
||||
// 解を記録
|
||||
res.append(path)
|
||||
}
|
||||
preOrder(root: root.left)
|
||||
preOrder(root: root.right)
|
||||
// バックトラック
|
||||
path.removeLast()
|
||||
}
|
||||
|
||||
@main
|
||||
enum PreorderTraversalIICompact {
|
||||
/* Driver Code */
|
||||
static func main() {
|
||||
let root = TreeNode.listToTree(arr: [1, 7, 3, 4, 5, 6, 7])
|
||||
print("\n二分木を初期化")
|
||||
PrintUtil.printTree(root: root)
|
||||
|
||||
// 先行順走査
|
||||
path = []
|
||||
res = []
|
||||
preOrder(root: root)
|
||||
|
||||
print("\n根ノードからノード 7 までのすべての経路を出力")
|
||||
for path in res {
|
||||
var vals: [Int] = []
|
||||
for node in path {
|
||||
vals.append(node.val)
|
||||
}
|
||||
print(vals)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
/**
|
||||
* File: preorder_traversal_iii_compact.swift
|
||||
* Created Time: 2023-04-30
|
||||
* Author: nuomi1 (nuomi1@qq.com)
|
||||
*/
|
||||
|
||||
import utils
|
||||
|
||||
var path: [TreeNode] = []
|
||||
var res: [[TreeNode]] = []
|
||||
|
||||
/* 前順走査:例題 3 */
|
||||
func preOrder(root: TreeNode?) {
|
||||
// 枝刈り
|
||||
guard let root = root, root.val != 3 else {
|
||||
return
|
||||
}
|
||||
// 試す
|
||||
path.append(root)
|
||||
if root.val == 7 {
|
||||
// 解を記録
|
||||
res.append(path)
|
||||
}
|
||||
preOrder(root: root.left)
|
||||
preOrder(root: root.right)
|
||||
// バックトラック
|
||||
path.removeLast()
|
||||
}
|
||||
|
||||
@main
|
||||
enum PreorderTraversalIIICompact {
|
||||
/* Driver Code */
|
||||
static func main() {
|
||||
let root = TreeNode.listToTree(arr: [1, 7, 3, 4, 5, 6, 7])
|
||||
print("\n二分木を初期化")
|
||||
PrintUtil.printTree(root: root)
|
||||
|
||||
// 先行順走査
|
||||
path = []
|
||||
res = []
|
||||
preOrder(root: root)
|
||||
|
||||
print("\n根ノードからノード 7 までのすべての経路を出力し、経路に値が 3 のノードを含まない")
|
||||
for path in res {
|
||||
var vals: [Int] = []
|
||||
for node in path {
|
||||
vals.append(node.val)
|
||||
}
|
||||
print(vals)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,76 @@
|
||||
/**
|
||||
* File: preorder_traversal_iii_template.swift
|
||||
* Created Time: 2023-04-30
|
||||
* Author: nuomi1 (nuomi1@qq.com)
|
||||
*/
|
||||
|
||||
import utils
|
||||
|
||||
/* 現在の状態が解かどうかを判定 */
|
||||
func isSolution(state: [TreeNode]) -> Bool {
|
||||
!state.isEmpty && state.last!.val == 7
|
||||
}
|
||||
|
||||
/* 解を記録 */
|
||||
func recordSolution(state: [TreeNode], res: inout [[TreeNode]]) {
|
||||
res.append(state)
|
||||
}
|
||||
|
||||
/* 現在の状態で、この選択が有効かどうかを判定 */
|
||||
func isValid(state: [TreeNode], choice: TreeNode?) -> Bool {
|
||||
choice != nil && choice!.val != 3
|
||||
}
|
||||
|
||||
/* 状態を更新 */
|
||||
func makeChoice(state: inout [TreeNode], choice: TreeNode) {
|
||||
state.append(choice)
|
||||
}
|
||||
|
||||
/* 状態を元に戻す */
|
||||
func undoChoice(state: inout [TreeNode], choice: TreeNode) {
|
||||
state.removeLast()
|
||||
}
|
||||
|
||||
/* バックトラッキング:例題 3 */
|
||||
func backtrack(state: inout [TreeNode], choices: [TreeNode], res: inout [[TreeNode]]) {
|
||||
// 解かどうかを確認
|
||||
if isSolution(state: state) {
|
||||
recordSolution(state: state, res: &res)
|
||||
}
|
||||
// すべての選択肢を走査
|
||||
for choice in choices {
|
||||
// 枝刈り:選択が妥当かを確認する
|
||||
if isValid(state: state, choice: choice) {
|
||||
// 試行: 選択を行い、状態を更新
|
||||
makeChoice(state: &state, choice: choice)
|
||||
// 次の選択へ進む
|
||||
backtrack(state: &state, choices: [choice.left, choice.right].compactMap { $0 }, res: &res)
|
||||
// バックトラック:選択を取り消し、前の状態に戻す
|
||||
undoChoice(state: &state, choice: choice)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@main
|
||||
enum PreorderTraversalIIITemplate {
|
||||
/* Driver Code */
|
||||
static func main() {
|
||||
let root = TreeNode.listToTree(arr: [1, 7, 3, 4, 5, 6, 7])
|
||||
print("\n二分木を初期化")
|
||||
PrintUtil.printTree(root: root)
|
||||
|
||||
// バックトラッキング法
|
||||
var state: [TreeNode] = []
|
||||
var res: [[TreeNode]] = []
|
||||
backtrack(state: &state, choices: [root].compactMap { $0 }, res: &res)
|
||||
|
||||
print("\n根ノードからノード 7 までのすべての経路を出力し、経路に値が 3 のノードを含まない")
|
||||
for path in res {
|
||||
var vals: [Int] = []
|
||||
for node in path {
|
||||
vals.append(node.val)
|
||||
}
|
||||
print(vals)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
/**
|
||||
* File: subset_sum_i.swift
|
||||
* Created Time: 2023-07-02
|
||||
* Author: nuomi1 (nuomi1@qq.com)
|
||||
*/
|
||||
|
||||
/* バックトラッキング:部分和 I */
|
||||
func backtrack(state: inout [Int], target: Int, choices: [Int], start: Int, res: inout [[Int]]) {
|
||||
// 部分集合の和が target に等しければ、解を記録
|
||||
if target == 0 {
|
||||
res.append(state)
|
||||
return
|
||||
}
|
||||
// すべての選択肢を走査
|
||||
// 枝刈り 2: start から走査し、重複する部分集合の生成を避ける
|
||||
for i in choices.indices.dropFirst(start) {
|
||||
// 枝刈り1:部分集合の和が target を超えたら、直ちにループを終了する
|
||||
// 配列はソート済みで後続要素のほうが大きく、部分集合の和は必ず target を超えるため
|
||||
if target - choices[i] < 0 {
|
||||
break
|
||||
}
|
||||
// 試す:選択を行い、target と start を更新
|
||||
state.append(choices[i])
|
||||
// 次の選択へ進む
|
||||
backtrack(state: &state, target: target - choices[i], choices: choices, start: i, res: &res)
|
||||
// バックトラック:選択を取り消し、前の状態に戻す
|
||||
state.removeLast()
|
||||
}
|
||||
}
|
||||
|
||||
/* 部分和 I を解く */
|
||||
func subsetSumI(nums: [Int], target: Int) -> [[Int]] {
|
||||
var state: [Int] = [] // 状態(部分集合)
|
||||
let nums = nums.sorted() // nums をソート
|
||||
let start = 0 // 開始点を走査
|
||||
var res: [[Int]] = [] // 結果リスト(部分集合のリスト)
|
||||
backtrack(state: &state, target: target, choices: nums, start: start, res: &res)
|
||||
return res
|
||||
}
|
||||
|
||||
@main
|
||||
enum SubsetSumI {
|
||||
/* Driver Code */
|
||||
static func main() {
|
||||
let nums = [3, 4, 5]
|
||||
let target = 9
|
||||
|
||||
let res = subsetSumI(nums: nums, target: target)
|
||||
|
||||
print("入力配列 nums = \(nums), target = \(target)")
|
||||
print("和が \(target) に等しいすべての部分集合 res = \(res)")
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
/**
|
||||
* File: subset_sum_i_naive.swift
|
||||
* Created Time: 2023-07-02
|
||||
* Author: nuomi1 (nuomi1@qq.com)
|
||||
*/
|
||||
|
||||
/* バックトラッキング:部分和 I */
|
||||
func backtrack(state: inout [Int], target: Int, total: Int, choices: [Int], res: inout [[Int]]) {
|
||||
// 部分集合の和が target に等しければ、解を記録
|
||||
if total == target {
|
||||
res.append(state)
|
||||
return
|
||||
}
|
||||
// すべての選択肢を走査
|
||||
for i in choices.indices {
|
||||
// 枝刈り:部分和が target を超える場合はその選択をスキップする
|
||||
if total + choices[i] > target {
|
||||
continue
|
||||
}
|
||||
// 試行:選択を行い、要素と total を更新する
|
||||
state.append(choices[i])
|
||||
// 次の選択へ進む
|
||||
backtrack(state: &state, target: target, total: total + choices[i], choices: choices, res: &res)
|
||||
// バックトラック:選択を取り消し、前の状態に戻す
|
||||
state.removeLast()
|
||||
}
|
||||
}
|
||||
|
||||
/* 部分和 I を解く(重複部分集合を含む) */
|
||||
func subsetSumINaive(nums: [Int], target: Int) -> [[Int]] {
|
||||
var state: [Int] = [] // 状態(部分集合)
|
||||
let total = 0 // 部分和
|
||||
var res: [[Int]] = [] // 結果リスト(部分集合のリスト)
|
||||
backtrack(state: &state, target: target, total: total, choices: nums, res: &res)
|
||||
return res
|
||||
}
|
||||
|
||||
@main
|
||||
enum SubsetSumINaive {
|
||||
/* Driver Code */
|
||||
static func main() {
|
||||
let nums = [3, 4, 5]
|
||||
let target = 9
|
||||
|
||||
let res = subsetSumINaive(nums: nums, target: target)
|
||||
|
||||
print("入力配列 nums = \(nums), target = \(target)")
|
||||
print("和が \(target) に等しいすべての部分集合 res = \(res)")
|
||||
print("この方法の出力結果には重複した集合が含まれることに注意してください")
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
/**
|
||||
* File: subset_sum_ii.swift
|
||||
* Created Time: 2023-07-02
|
||||
* Author: nuomi1 (nuomi1@qq.com)
|
||||
*/
|
||||
|
||||
/* バックトラッキング:部分和 II */
|
||||
func backtrack(state: inout [Int], target: Int, choices: [Int], start: Int, res: inout [[Int]]) {
|
||||
// 部分集合の和が target に等しければ、解を記録
|
||||
if target == 0 {
|
||||
res.append(state)
|
||||
return
|
||||
}
|
||||
// すべての選択肢を走査
|
||||
// 枝刈り 2: start から走査し、重複する部分集合の生成を避ける
|
||||
// 枝刈り 3: start から走査し、同じ要素の重複選択を避ける
|
||||
for i in choices.indices.dropFirst(start) {
|
||||
// 枝刈り1:部分集合の和が target を超えたら、直ちにループを終了する
|
||||
// 配列はソート済みで後続要素のほうが大きく、部分集合の和は必ず target を超えるため
|
||||
if target - choices[i] < 0 {
|
||||
break
|
||||
}
|
||||
// 枝刈り4:この要素が左隣の要素と等しければ、その探索分岐は重複しているためスキップする
|
||||
if i > start, choices[i] == choices[i - 1] {
|
||||
continue
|
||||
}
|
||||
// 試す:選択を行い、target と start を更新
|
||||
state.append(choices[i])
|
||||
// 次の選択へ進む
|
||||
backtrack(state: &state, target: target - choices[i], choices: choices, start: i + 1, res: &res)
|
||||
// バックトラック:選択を取り消し、前の状態に戻す
|
||||
state.removeLast()
|
||||
}
|
||||
}
|
||||
|
||||
/* 部分和 II を解く */
|
||||
func subsetSumII(nums: [Int], target: Int) -> [[Int]] {
|
||||
var state: [Int] = [] // 状態(部分集合)
|
||||
let nums = nums.sorted() // nums をソート
|
||||
let start = 0 // 開始点を走査
|
||||
var res: [[Int]] = [] // 結果リスト(部分集合のリスト)
|
||||
backtrack(state: &state, target: target, choices: nums, start: start, res: &res)
|
||||
return res
|
||||
}
|
||||
|
||||
@main
|
||||
enum SubsetSumII {
|
||||
/* Driver Code */
|
||||
static func main() {
|
||||
let nums = [4, 4, 5]
|
||||
let target = 9
|
||||
|
||||
let res = subsetSumII(nums: nums, target: target)
|
||||
|
||||
print("入力配列 nums = \(nums), target = \(target)")
|
||||
print("和が \(target) に等しいすべての部分集合 res = \(res)")
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
/**
|
||||
* File: iteration.swift
|
||||
* Created Time: 2023-09-02
|
||||
* Author: nuomi1 (nuomi1@qq.com)
|
||||
*/
|
||||
|
||||
/* for ループ */
|
||||
func forLoop(n: Int) -> Int {
|
||||
var res = 0
|
||||
// 1, 2, ..., n-1, n を順に加算する
|
||||
for i in 1 ... n {
|
||||
res += i
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
/* while ループ */
|
||||
func whileLoop(n: Int) -> Int {
|
||||
var res = 0
|
||||
var i = 1 // 条件変数を初期化する
|
||||
// 1, 2, ..., n-1, n を順に加算する
|
||||
while i <= n {
|
||||
res += i
|
||||
i += 1 // 条件変数を更新する
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
/* while ループ(2回更新) */
|
||||
func whileLoopII(n: Int) -> Int {
|
||||
var res = 0
|
||||
var i = 1 // 条件変数を初期化する
|
||||
// 1, 4, 10, ... を順に加算する
|
||||
while i <= n {
|
||||
res += i
|
||||
// 条件変数を更新する
|
||||
i += 1
|
||||
i *= 2
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
/* 二重 for ループ */
|
||||
func nestedForLoop(n: Int) -> String {
|
||||
var res = ""
|
||||
// i = 1, 2, ..., n-1, n とループする
|
||||
for i in 1 ... n {
|
||||
// j = 1, 2, ..., n-1, n とループする
|
||||
for j in 1 ... n {
|
||||
res.append("(\(i), \(j)), ")
|
||||
}
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
@main
|
||||
enum Iteration {
|
||||
/* Driver Code */
|
||||
static func main() {
|
||||
let n = 5
|
||||
var res = 0
|
||||
|
||||
res = forLoop(n: n)
|
||||
print("\nfor ループの合計結果 res = \(res)")
|
||||
|
||||
res = whileLoop(n: n)
|
||||
print("\nwhile ループの合計結果 res = \(res)")
|
||||
|
||||
res = whileLoopII(n: n)
|
||||
print("\nwhile ループ(2 回更新)の合計結果 res = \(res)")
|
||||
|
||||
let resStr = nestedForLoop(n: n)
|
||||
print("\n二重 for ループの走査結果 \(resStr)")
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,79 @@
|
||||
/**
|
||||
* File: recursion.swift
|
||||
* Created Time: 2023-09-02
|
||||
* Author: nuomi1 (nuomi1@qq.com)
|
||||
*/
|
||||
|
||||
/* 再帰 */
|
||||
func recur(n: Int) -> Int {
|
||||
// 終了条件
|
||||
if n == 1 {
|
||||
return 1
|
||||
}
|
||||
// 再帰:再帰呼び出し
|
||||
let res = recur(n: n - 1)
|
||||
// 帰りがけ:結果を返す
|
||||
return n + res
|
||||
}
|
||||
|
||||
/* 反復で再帰を模擬する */
|
||||
func forLoopRecur(n: Int) -> Int {
|
||||
// 明示的なスタックを使ってシステムコールスタックを模擬する
|
||||
var stack: [Int] = []
|
||||
var res = 0
|
||||
// 再帰:再帰呼び出し
|
||||
for i in (1 ... n).reversed() {
|
||||
// 「スタックへのプッシュ」で「再帰」を模擬する
|
||||
stack.append(i)
|
||||
}
|
||||
// 帰りがけ:結果を返す
|
||||
while !stack.isEmpty {
|
||||
// 「スタックから取り出す操作」で「帰り」をシミュレート
|
||||
res += stack.removeLast()
|
||||
}
|
||||
// res = 1+2+3+...+n
|
||||
return res
|
||||
}
|
||||
|
||||
/* 末尾再帰 */
|
||||
func tailRecur(n: Int, res: Int) -> Int {
|
||||
// 終了条件
|
||||
if n == 0 {
|
||||
return res
|
||||
}
|
||||
// 末尾再帰呼び出し
|
||||
return tailRecur(n: n - 1, res: res + n)
|
||||
}
|
||||
|
||||
/* フィボナッチ数列:再帰 */
|
||||
func fib(n: Int) -> Int {
|
||||
// 終了条件 f(1) = 0, f(2) = 1
|
||||
if n == 1 || n == 2 {
|
||||
return n - 1
|
||||
}
|
||||
// f(n) = f(n-1) + f(n-2) を再帰的に呼び出す
|
||||
let res = fib(n: n - 1) + fib(n: n - 2)
|
||||
// 結果 f(n) を返す
|
||||
return res
|
||||
}
|
||||
|
||||
@main
|
||||
enum Recursion {
|
||||
/* Driver Code */
|
||||
static func main() {
|
||||
let n = 5
|
||||
var res = 0
|
||||
|
||||
res = recursion.recur(n: n)
|
||||
print("\n再帰関数による総和結果 res = \(res)")
|
||||
|
||||
res = recursion.forLoopRecur(n: n)
|
||||
print("\n反復で再帰をシミュレートした総和結果 res = \(res)")
|
||||
|
||||
res = recursion.tailRecur(n: n, res: 0)
|
||||
print("\n末尾再帰関数による総和結果 res = \(res)")
|
||||
|
||||
res = recursion.fib(n: n)
|
||||
print("\nフィボナッチ数列の第 \(n) 項は \(res)")
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,98 @@
|
||||
/**
|
||||
* File: space_complexity.swift
|
||||
* Created Time: 2023-01-01
|
||||
* Author: nuomi1 (nuomi1@qq.com)
|
||||
*/
|
||||
|
||||
import utils
|
||||
|
||||
/* 関数 */
|
||||
@discardableResult
|
||||
func function() -> Int {
|
||||
// 何らかの処理を行う
|
||||
return 0
|
||||
}
|
||||
|
||||
/* 定数階 */
|
||||
func constant(n: Int) {
|
||||
// 定数、変数、オブジェクトは O(1) の空間を占める
|
||||
let a = 0
|
||||
var b = 0
|
||||
let nums = Array(repeating: 0, count: 10000)
|
||||
let node = ListNode(x: 0)
|
||||
// ループ内の変数は O(1) の空間を占める
|
||||
for _ in 0 ..< n {
|
||||
let c = 0
|
||||
}
|
||||
// ループ内の関数は O(1) の空間を占める
|
||||
for _ in 0 ..< n {
|
||||
function()
|
||||
}
|
||||
}
|
||||
|
||||
/* 線形階 */
|
||||
func linear(n: Int) {
|
||||
// 長さ n の配列は O(n) の空間を使用
|
||||
let nums = Array(repeating: 0, count: n)
|
||||
// 長さ n のリストは O(n) の空間を使用
|
||||
let nodes = (0 ..< n).map { ListNode(x: $0) }
|
||||
// 長さ n のハッシュテーブルは O(n) の空間を使用
|
||||
let map = Dictionary(uniqueKeysWithValues: (0 ..< n).map { ($0, "\($0)") })
|
||||
}
|
||||
|
||||
/* 線形時間(再帰実装) */
|
||||
func linearRecur(n: Int) {
|
||||
print("再帰 n = \(n)")
|
||||
if n == 1 {
|
||||
return
|
||||
}
|
||||
linearRecur(n: n - 1)
|
||||
}
|
||||
|
||||
/* 二乗階 */
|
||||
func quadratic(n: Int) {
|
||||
// 二次元リストは O(n^2) の空間を使用
|
||||
let numList = Array(repeating: Array(repeating: 0, count: n), count: n)
|
||||
}
|
||||
|
||||
/* 二次時間(再帰実装) */
|
||||
@discardableResult
|
||||
func quadraticRecur(n: Int) -> Int {
|
||||
if n <= 0 {
|
||||
return 0
|
||||
}
|
||||
// 配列 nums の長さは n, n-1, ..., 2, 1
|
||||
let nums = Array(repeating: 0, count: n)
|
||||
print("再帰 n = \(n) における nums の長さ = \(nums.count)")
|
||||
return quadraticRecur(n: n - 1)
|
||||
}
|
||||
|
||||
/* 指数時間(完全二分木の構築) */
|
||||
func buildTree(n: Int) -> TreeNode? {
|
||||
if n == 0 {
|
||||
return nil
|
||||
}
|
||||
let root = TreeNode(x: 0)
|
||||
root.left = buildTree(n: n - 1)
|
||||
root.right = buildTree(n: n - 1)
|
||||
return root
|
||||
}
|
||||
|
||||
@main
|
||||
enum SpaceComplexity {
|
||||
/* Driver Code */
|
||||
static func main() {
|
||||
let n = 5
|
||||
// 定数階
|
||||
constant(n: n)
|
||||
// 線形階
|
||||
linear(n: n)
|
||||
linearRecur(n: n)
|
||||
// 二乗階
|
||||
quadratic(n: n)
|
||||
quadraticRecur(n: n)
|
||||
// 指数オーダー
|
||||
let root = buildTree(n: n)
|
||||
PrintUtil.printTree(root: root)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,172 @@
|
||||
/**
|
||||
* File: time_complexity.swift
|
||||
* Created Time: 2022-12-26
|
||||
* Author: nuomi1 (nuomi1@qq.com)
|
||||
*/
|
||||
|
||||
/* 定数階 */
|
||||
func constant(n: Int) -> Int {
|
||||
var count = 0
|
||||
let size = 100_000
|
||||
for _ in 0 ..< size {
|
||||
count += 1
|
||||
}
|
||||
return count
|
||||
}
|
||||
|
||||
/* 線形階 */
|
||||
func linear(n: Int) -> Int {
|
||||
var count = 0
|
||||
for _ in 0 ..< n {
|
||||
count += 1
|
||||
}
|
||||
return count
|
||||
}
|
||||
|
||||
/* 線形時間(配列を走査) */
|
||||
func arrayTraversal(nums: [Int]) -> Int {
|
||||
var count = 0
|
||||
// ループ回数は配列長に比例する
|
||||
for _ in nums {
|
||||
count += 1
|
||||
}
|
||||
return count
|
||||
}
|
||||
|
||||
/* 二乗階 */
|
||||
func quadratic(n: Int) -> Int {
|
||||
var count = 0
|
||||
// ループ回数はデータサイズ n の二乗に比例する
|
||||
for _ in 0 ..< n {
|
||||
for _ in 0 ..< n {
|
||||
count += 1
|
||||
}
|
||||
}
|
||||
return count
|
||||
}
|
||||
|
||||
/* 二次時間(バブルソート) */
|
||||
func bubbleSort(nums: inout [Int]) -> Int {
|
||||
var count = 0 // カウンタ
|
||||
// 外側のループ:未ソート区間は [0, i]
|
||||
for i in nums.indices.dropFirst().reversed() {
|
||||
// 内側のループ:未ソート区間 [0, i] の最大要素をその区間の最右端へ交換
|
||||
for j in 0 ..< i {
|
||||
if nums[j] > nums[j + 1] {
|
||||
// nums[j] と nums[j + 1] を交換
|
||||
let tmp = nums[j]
|
||||
nums[j] = nums[j + 1]
|
||||
nums[j + 1] = tmp
|
||||
count += 3 // 要素交換には 3 回の単位操作が含まれる
|
||||
}
|
||||
}
|
||||
}
|
||||
return count
|
||||
}
|
||||
|
||||
/* 指数時間(ループ実装) */
|
||||
func exponential(n: Int) -> Int {
|
||||
var count = 0
|
||||
var base = 1
|
||||
// 細胞は各ラウンドで 2 つに分裂し、数列 1, 2, 4, 8, ..., 2^(n-1) を形成する
|
||||
for _ in 0 ..< n {
|
||||
for _ in 0 ..< base {
|
||||
count += 1
|
||||
}
|
||||
base *= 2
|
||||
}
|
||||
// count = 1 + 2 + 4 + 8 + .. + 2^(n-1) = 2^n - 1
|
||||
return count
|
||||
}
|
||||
|
||||
/* 指数時間(再帰実装) */
|
||||
func expRecur(n: Int) -> Int {
|
||||
if n == 1 {
|
||||
return 1
|
||||
}
|
||||
return expRecur(n: n - 1) + expRecur(n: n - 1) + 1
|
||||
}
|
||||
|
||||
/* 対数時間(ループ実装) */
|
||||
func logarithmic(n: Int) -> Int {
|
||||
var count = 0
|
||||
var n = n
|
||||
while n > 1 {
|
||||
n = n / 2
|
||||
count += 1
|
||||
}
|
||||
return count
|
||||
}
|
||||
|
||||
/* 対数時間(再帰実装) */
|
||||
func logRecur(n: Int) -> Int {
|
||||
if n <= 1 {
|
||||
return 0
|
||||
}
|
||||
return logRecur(n: n / 2) + 1
|
||||
}
|
||||
|
||||
/* 線形対数時間 */
|
||||
func linearLogRecur(n: Int) -> Int {
|
||||
if n <= 1 {
|
||||
return 1
|
||||
}
|
||||
var count = linearLogRecur(n: n / 2) + linearLogRecur(n: n / 2)
|
||||
for _ in stride(from: 0, to: n, by: 1) {
|
||||
count += 1
|
||||
}
|
||||
return count
|
||||
}
|
||||
|
||||
/* 階乗時間(再帰実装) */
|
||||
func factorialRecur(n: Int) -> Int {
|
||||
if n == 0 {
|
||||
return 1
|
||||
}
|
||||
var count = 0
|
||||
// 1個から n 個に分裂
|
||||
for _ in 0 ..< n {
|
||||
count += factorialRecur(n: n - 1)
|
||||
}
|
||||
return count
|
||||
}
|
||||
|
||||
@main
|
||||
enum TimeComplexity {
|
||||
/* Driver Code */
|
||||
static func main() {
|
||||
// n を変えて実行し、各計算量で操作回数がどう変化するかを確認できる
|
||||
let n = 8
|
||||
print("入力データサイズ n = \(n)")
|
||||
|
||||
var count = constant(n: n)
|
||||
print("定数時間の操作回数 = \(count)")
|
||||
|
||||
count = linear(n: n)
|
||||
print("線形時間の操作回数 = \(count)")
|
||||
count = arrayTraversal(nums: Array(repeating: 0, count: n))
|
||||
print("線形時間(配列の走査)の操作回数 = \(count)")
|
||||
|
||||
count = quadratic(n: n)
|
||||
print("二乗時間の操作回数 = \(count)")
|
||||
var nums = Array(stride(from: n, to: 0, by: -1)) // [n,n-1,...,2,1]
|
||||
count = bubbleSort(nums: &nums)
|
||||
print("二乗時間(バブルソート)の操作回数 = \(count)")
|
||||
|
||||
count = exponential(n: n)
|
||||
print("指数時間(ループ実装)の操作回数 = \(count)")
|
||||
count = expRecur(n: n)
|
||||
print("指数時間(再帰実装)の操作回数 = \(count)")
|
||||
|
||||
count = logarithmic(n: n)
|
||||
print("対数時間(ループ実装)の操作回数 = \(count)")
|
||||
count = logRecur(n: n)
|
||||
print("対数時間(再帰実装)の操作回数 = \(count)")
|
||||
|
||||
count = linearLogRecur(n: n)
|
||||
print("線形対数時間(再帰実装)の操作回数 = \(count)")
|
||||
|
||||
count = factorialRecur(n: n)
|
||||
print("階乗時間(再帰実装)の操作回数 = \(count)")
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
/**
|
||||
* File: worst_best_time_complexity.swift
|
||||
* Created Time: 2022-12-26
|
||||
* Author: nuomi1 (nuomi1@qq.com)
|
||||
*/
|
||||
|
||||
/* 要素が { 1, 2, ..., n } で、順序がシャッフルされた配列を生成 */
|
||||
func randomNumbers(n: Int) -> [Int] {
|
||||
// 配列 nums = { 1, 2, 3, ..., n } を生成
|
||||
var nums = Array(1 ... n)
|
||||
// 配列要素をランダムにシャッフル
|
||||
nums.shuffle()
|
||||
return nums
|
||||
}
|
||||
|
||||
/* 配列 nums 内で数値 1 のインデックスを探す */
|
||||
func findOne(nums: [Int]) -> Int {
|
||||
for i in nums.indices {
|
||||
// 要素 1 が配列の先頭にあるとき、最良時間計算量 O(1) となる
|
||||
// 要素 1 が配列の末尾にあるとき、最悪時間計算量 O(n) となる
|
||||
if nums[i] == 1 {
|
||||
return i
|
||||
}
|
||||
}
|
||||
return -1
|
||||
}
|
||||
|
||||
@main
|
||||
enum WorstBestTimeComplexity {
|
||||
/* Driver Code */
|
||||
static func main() {
|
||||
for _ in 0 ..< 10 {
|
||||
let n = 100
|
||||
let nums = randomNumbers(n: n)
|
||||
let index = findOne(nums: nums)
|
||||
print("配列 [ 1, 2, ..., n ] をシャッフルした後 = \(nums)")
|
||||
print("数値 1 のインデックスは \(index)")
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
/**
|
||||
* File: binary_search_recur.swift
|
||||
* Created Time: 2023-09-02
|
||||
* Author: nuomi1 (nuomi1@qq.com)
|
||||
*/
|
||||
|
||||
/* 二分探索:問題 f(i, j) */
|
||||
func dfs(nums: [Int], target: Int, i: Int, j: Int) -> Int {
|
||||
// 区間が空なら対象要素は存在しないので -1 を返す
|
||||
if i > j {
|
||||
return -1
|
||||
}
|
||||
// 中点インデックス m を計算
|
||||
let m = (i + j) / 2
|
||||
if nums[m] < target {
|
||||
// 部分問題 f(m+1, j) を再帰的に解く
|
||||
return dfs(nums: nums, target: target, i: m + 1, j: j)
|
||||
} else if nums[m] > target {
|
||||
// 部分問題 f(i, m-1) を再帰的に解く
|
||||
return dfs(nums: nums, target: target, i: i, j: m - 1)
|
||||
} else {
|
||||
// 目標要素が見つかったらそのインデックスを返す
|
||||
return m
|
||||
}
|
||||
}
|
||||
|
||||
/* 二分探索 */
|
||||
func binarySearch(nums: [Int], target: Int) -> Int {
|
||||
// 問題 f(0, n-1) を解く
|
||||
dfs(nums: nums, target: target, i: nums.startIndex, j: nums.endIndex - 1)
|
||||
}
|
||||
|
||||
@main
|
||||
enum BinarySearchRecur {
|
||||
/* Driver Code */
|
||||
static func main() {
|
||||
let target = 6
|
||||
let nums = [1, 3, 6, 8, 12, 15, 23, 26, 31, 35]
|
||||
|
||||
// 二分探索(両閉区間)
|
||||
let index = binarySearch(nums: nums, target: target)
|
||||
print("対象要素 6 のインデックス = \(index)")
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
/**
|
||||
* File: build_tree.swift
|
||||
* Created Time: 2023-09-02
|
||||
* Author: nuomi1 (nuomi1@qq.com)
|
||||
*/
|
||||
|
||||
import utils
|
||||
|
||||
/* 二分木を構築:分割統治 */
|
||||
func dfs(preorder: [Int], inorderMap: [Int: Int], i: Int, l: Int, r: Int) -> TreeNode? {
|
||||
// 部分木区間が空なら終了する
|
||||
if r - l < 0 {
|
||||
return nil
|
||||
}
|
||||
// ルートノードを初期化する
|
||||
let root = TreeNode(x: preorder[i])
|
||||
// m を求めて左右部分木を分割する
|
||||
let m = inorderMap[preorder[i]]!
|
||||
// 部分問題:左部分木を構築する
|
||||
root.left = dfs(preorder: preorder, inorderMap: inorderMap, i: i + 1, l: l, r: m - 1)
|
||||
// 部分問題:右部分木を構築する
|
||||
root.right = dfs(preorder: preorder, inorderMap: inorderMap, i: i + 1 + m - l, l: m + 1, r: r)
|
||||
// 根ノードを返す
|
||||
return root
|
||||
}
|
||||
|
||||
/* 二分木を構築 */
|
||||
func buildTree(preorder: [Int], inorder: [Int]) -> TreeNode? {
|
||||
// inorder の要素からインデックスへの対応を格納するハッシュテーブルを初期化する
|
||||
let inorderMap = inorder.enumerated().reduce(into: [:]) { $0[$1.element] = $1.offset }
|
||||
return dfs(preorder: preorder, inorderMap: inorderMap, i: inorder.startIndex, l: inorder.startIndex, r: inorder.endIndex - 1)
|
||||
}
|
||||
|
||||
@main
|
||||
enum BuildTree {
|
||||
/* Driver Code */
|
||||
static func main() {
|
||||
let preorder = [3, 9, 2, 1, 7]
|
||||
let inorder = [9, 3, 1, 2, 7]
|
||||
print("前順走査 = \(preorder)")
|
||||
print("中順走査 = \(inorder)")
|
||||
|
||||
let root = buildTree(preorder: preorder, inorder: inorder)
|
||||
print("構築した二分木は:")
|
||||
PrintUtil.printTree(root: root)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
/**
|
||||
* File: hanota.swift
|
||||
* Created Time: 2023-09-02
|
||||
* Author: nuomi1 (nuomi1@qq.com)
|
||||
*/
|
||||
|
||||
/* 円盤を 1 枚移動 */
|
||||
func move(src: inout [Int], tar: inout [Int]) {
|
||||
// src の上から円盤を1枚取り出す
|
||||
let pan = src.popLast()!
|
||||
// 円盤を tar の上に置く
|
||||
tar.append(pan)
|
||||
}
|
||||
|
||||
/* ハノイの塔の問題 f(i) を解く */
|
||||
func dfs(i: Int, src: inout [Int], buf: inout [Int], tar: inout [Int]) {
|
||||
// src に円盤が 1 枚だけ残っている場合は、そのまま tar へ移す
|
||||
if i == 1 {
|
||||
move(src: &src, tar: &tar)
|
||||
return
|
||||
}
|
||||
// 部分問題 f(i-1):src の上部 i-1 枚の円盤を tar を補助にして buf へ移す
|
||||
dfs(i: i - 1, src: &src, buf: &tar, tar: &buf)
|
||||
// 部分問題 f(1):src に残る 1 枚の円盤を tar に移す
|
||||
move(src: &src, tar: &tar)
|
||||
// 部分問題 f(i-1):buf の上部 i-1 枚の円盤を src を補助にして tar へ移す
|
||||
dfs(i: i - 1, src: &buf, buf: &src, tar: &tar)
|
||||
}
|
||||
|
||||
/* ハノイの塔を解く */
|
||||
func solveHanota(A: inout [Int], B: inout [Int], C: inout [Int]) {
|
||||
let n = A.count
|
||||
// リストの末尾が柱の上端に対応する
|
||||
// src の上から n 個の円盤を、B を介して C に移動する
|
||||
dfs(i: n, src: &A, buf: &B, tar: &C)
|
||||
}
|
||||
|
||||
@main
|
||||
enum Hanota {
|
||||
/* Driver Code */
|
||||
static func main() {
|
||||
// リスト末尾が柱の頂上
|
||||
var A = [5, 4, 3, 2, 1]
|
||||
var B: [Int] = []
|
||||
var C: [Int] = []
|
||||
print("初期状態:")
|
||||
print("A = \(A)")
|
||||
print("B = \(B)")
|
||||
print("C = \(C)")
|
||||
|
||||
solveHanota(A: &A, B: &B, C: &C)
|
||||
|
||||
print("円盤の移動完了後:")
|
||||
print("A = \(A)")
|
||||
print("B = \(B)")
|
||||
print("C = \(C)")
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
/**
|
||||
* File: climbing_stairs_backtrack.swift
|
||||
* Created Time: 2023-07-15
|
||||
* Author: nuomi1 (nuomi1@qq.com)
|
||||
*/
|
||||
|
||||
/* バックトラッキング */
|
||||
func backtrack(choices: [Int], state: Int, n: Int, res: inout [Int]) {
|
||||
// 第 n 段に到達したら、方法数を 1 増やす
|
||||
if state == n {
|
||||
res[0] += 1
|
||||
}
|
||||
// すべての選択肢を走査
|
||||
for choice in choices {
|
||||
// 枝刈り: 第 n 段を超えないようにする
|
||||
if state + choice > n {
|
||||
continue
|
||||
}
|
||||
// 試行: 選択を行い、状態を更新
|
||||
backtrack(choices: choices, state: state + choice, n: n, res: &res)
|
||||
// バックトラック
|
||||
}
|
||||
}
|
||||
|
||||
/* 階段登り:バックトラッキング */
|
||||
func climbingStairsBacktrack(n: Int) -> Int {
|
||||
let choices = [1, 2] // 1 段または 2 段上ることを選べる
|
||||
let state = 0 // 第 0 段から上り始める
|
||||
var res: [Int] = []
|
||||
res.append(0) // res[0] を使って方法数を記録する
|
||||
backtrack(choices: choices, state: state, n: n, res: &res)
|
||||
return res[0]
|
||||
}
|
||||
|
||||
@main
|
||||
enum ClimbingStairsBacktrack {
|
||||
/* Driver Code */
|
||||
static func main() {
|
||||
let n = 9
|
||||
|
||||
let res = climbingStairsBacktrack(n: n)
|
||||
print("\(n) 段の階段を上る方法は全部で \(res) 通り")
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
/**
|
||||
* File: climbing_stairs_constraint_dp.swift
|
||||
* Created Time: 2023-07-15
|
||||
* Author: nuomi1 (nuomi1@qq.com)
|
||||
*/
|
||||
|
||||
/* 制約付き階段登り:動的計画法 */
|
||||
func climbingStairsConstraintDP(n: Int) -> Int {
|
||||
if n == 1 || n == 2 {
|
||||
return 1
|
||||
}
|
||||
// 部分問題の解を保存するために dp テーブルを初期化
|
||||
var dp = Array(repeating: Array(repeating: 0, count: 3), count: n + 1)
|
||||
// 初期状態:最小部分問題の解をあらかじめ設定
|
||||
dp[1][1] = 1
|
||||
dp[1][2] = 0
|
||||
dp[2][1] = 0
|
||||
dp[2][2] = 1
|
||||
// 状態遷移:小さい部分問題から大きい部分問題へ順に解く
|
||||
for i in 3 ... n {
|
||||
dp[i][1] = dp[i - 1][2]
|
||||
dp[i][2] = dp[i - 2][1] + dp[i - 2][2]
|
||||
}
|
||||
return dp[n][1] + dp[n][2]
|
||||
}
|
||||
|
||||
@main
|
||||
enum ClimbingStairsConstraintDP {
|
||||
/* Driver Code */
|
||||
static func main() {
|
||||
let n = 9
|
||||
|
||||
let res = climbingStairsConstraintDP(n: n)
|
||||
print("\(n) 段の階段を上る方法は全部で \(res) 通り")
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
/**
|
||||
* File: climbing_stairs_dfs.swift
|
||||
* Created Time: 2023-07-15
|
||||
* Author: nuomi1 (nuomi1@qq.com)
|
||||
*/
|
||||
|
||||
/* 検索 */
|
||||
func dfs(i: Int) -> Int {
|
||||
// dp[1] と dp[2] は既知なので返す
|
||||
if i == 1 || i == 2 {
|
||||
return i
|
||||
}
|
||||
// dp[i] = dp[i-1] + dp[i-2]
|
||||
let count = dfs(i: i - 1) + dfs(i: i - 2)
|
||||
return count
|
||||
}
|
||||
|
||||
/* 階段登り:探索 */
|
||||
func climbingStairsDFS(n: Int) -> Int {
|
||||
dfs(i: n)
|
||||
}
|
||||
|
||||
@main
|
||||
enum ClimbingStairsDFS {
|
||||
/* Driver Code */
|
||||
static func main() {
|
||||
let n = 9
|
||||
|
||||
let res = climbingStairsDFS(n: n)
|
||||
print("\(n) 段の階段を上る方法は全部で \(res) 通り")
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
/**
|
||||
* File: climbing_stairs_dfs_mem.swift
|
||||
* Created Time: 2023-07-15
|
||||
* Author: nuomi1 (nuomi1@qq.com)
|
||||
*/
|
||||
|
||||
/* メモ化探索 */
|
||||
func dfs(i: Int, mem: inout [Int]) -> Int {
|
||||
// dp[1] と dp[2] は既知なので返す
|
||||
if i == 1 || i == 2 {
|
||||
return i
|
||||
}
|
||||
// dp[i] の記録があれば、それをそのまま返す
|
||||
if mem[i] != -1 {
|
||||
return mem[i]
|
||||
}
|
||||
// dp[i] = dp[i-1] + dp[i-2]
|
||||
let count = dfs(i: i - 1, mem: &mem) + dfs(i: i - 2, mem: &mem)
|
||||
// dp[i] を記録する
|
||||
mem[i] = count
|
||||
return count
|
||||
}
|
||||
|
||||
/* 階段登り:メモ化探索 */
|
||||
func climbingStairsDFSMem(n: Int) -> Int {
|
||||
// mem[i] は第 i 段まで上る方法の総数を記録し、-1 は未記録を表す
|
||||
var mem = Array(repeating: -1, count: n + 1)
|
||||
return dfs(i: n, mem: &mem)
|
||||
}
|
||||
|
||||
@main
|
||||
enum ClimbingStairsDFSMem {
|
||||
/* Driver Code */
|
||||
static func main() {
|
||||
let n = 9
|
||||
|
||||
let res = climbingStairsDFSMem(n: n)
|
||||
print("\(n) 段の階段を上る方法は全部で \(res) 通り")
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
/**
|
||||
* File: climbing_stairs_dp.swift
|
||||
* Created Time: 2023-07-15
|
||||
* Author: nuomi1 (nuomi1@qq.com)
|
||||
*/
|
||||
|
||||
/* 階段登り:動的計画法 */
|
||||
func climbingStairsDP(n: Int) -> Int {
|
||||
if n == 1 || n == 2 {
|
||||
return n
|
||||
}
|
||||
// 部分問題の解を保存するために dp テーブルを初期化
|
||||
var dp = Array(repeating: 0, count: n + 1)
|
||||
// 初期状態:最小部分問題の解をあらかじめ設定
|
||||
dp[1] = 1
|
||||
dp[2] = 2
|
||||
// 状態遷移:小さい部分問題から大きい部分問題へ順に解く
|
||||
for i in 3 ... n {
|
||||
dp[i] = dp[i - 1] + dp[i - 2]
|
||||
}
|
||||
return dp[n]
|
||||
}
|
||||
|
||||
/* 階段登り:空間最適化した動的計画法 */
|
||||
func climbingStairsDPComp(n: Int) -> Int {
|
||||
if n == 1 || n == 2 {
|
||||
return n
|
||||
}
|
||||
var a = 1
|
||||
var b = 2
|
||||
for _ in 3 ... n {
|
||||
(a, b) = (b, a + b)
|
||||
}
|
||||
return b
|
||||
}
|
||||
|
||||
@main
|
||||
enum ClimbingStairsDP {
|
||||
/* Driver Code */
|
||||
static func main() {
|
||||
let n = 9
|
||||
|
||||
var res = climbingStairsDP(n: n)
|
||||
print("\(n) 段の階段を上る方法は全部で \(res) 通り")
|
||||
|
||||
res = climbingStairsDPComp(n: n)
|
||||
print("\(n) 段の階段を上る方法は全部で \(res) 通り")
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
/**
|
||||
* File: coin_change.swift
|
||||
* Created Time: 2023-07-15
|
||||
* Author: nuomi1 (nuomi1@qq.com)
|
||||
*/
|
||||
|
||||
/* コイン両替:動的計画法 */
|
||||
func coinChangeDP(coins: [Int], amt: Int) -> Int {
|
||||
let n = coins.count
|
||||
let MAX = amt + 1
|
||||
// dp テーブルを初期化
|
||||
var dp = Array(repeating: Array(repeating: 0, count: amt + 1), count: n + 1)
|
||||
// 状態遷移:先頭行と先頭列
|
||||
for a in 1 ... amt {
|
||||
dp[0][a] = MAX
|
||||
}
|
||||
// 状態遷移: 残りの行と列
|
||||
for i in 1 ... n {
|
||||
for a in 1 ... amt {
|
||||
if coins[i - 1] > a {
|
||||
// 目標金額を超えるなら硬貨 i は選ばない
|
||||
dp[i][a] = dp[i - 1][a]
|
||||
} else {
|
||||
// 硬貨 i を選ばない場合と選ぶ場合の小さい方
|
||||
dp[i][a] = min(dp[i - 1][a], dp[i][a - coins[i - 1]] + 1)
|
||||
}
|
||||
}
|
||||
}
|
||||
return dp[n][amt] != MAX ? dp[n][amt] : -1
|
||||
}
|
||||
|
||||
/* コイン交換:空間最適化後の動的計画法 */
|
||||
func coinChangeDPComp(coins: [Int], amt: Int) -> Int {
|
||||
let n = coins.count
|
||||
let MAX = amt + 1
|
||||
// dp テーブルを初期化
|
||||
var dp = Array(repeating: MAX, count: amt + 1)
|
||||
dp[0] = 0
|
||||
// 状態遷移
|
||||
for i in 1 ... n {
|
||||
for a in 1 ... amt {
|
||||
if coins[i - 1] > a {
|
||||
// 目標金額を超えるなら硬貨 i は選ばない
|
||||
dp[a] = dp[a]
|
||||
} else {
|
||||
// 硬貨 i を選ばない場合と選ぶ場合の小さい方
|
||||
dp[a] = min(dp[a], dp[a - coins[i - 1]] + 1)
|
||||
}
|
||||
}
|
||||
}
|
||||
return dp[amt] != MAX ? dp[amt] : -1
|
||||
}
|
||||
|
||||
@main
|
||||
enum CoinChange {
|
||||
/* Driver Code */
|
||||
static func main() {
|
||||
let coins = [1, 2, 5]
|
||||
let amt = 4
|
||||
|
||||
// 動的計画法
|
||||
var res = coinChangeDP(coins: coins, amt: amt)
|
||||
print("目標金額に必要な最小硬貨枚数は \(res)")
|
||||
|
||||
// 空間最適化後の動的計画法
|
||||
res = coinChangeDPComp(coins: coins, amt: amt)
|
||||
print("目標金額に必要な最小硬貨枚数は \(res)")
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
/**
|
||||
* File: coin_change_ii.swift
|
||||
* Created Time: 2023-07-16
|
||||
* Author: nuomi1 (nuomi1@qq.com)
|
||||
*/
|
||||
|
||||
/* コイン両替 II:動的計画法 */
|
||||
func coinChangeIIDP(coins: [Int], amt: Int) -> Int {
|
||||
let n = coins.count
|
||||
// dp テーブルを初期化
|
||||
var dp = Array(repeating: Array(repeating: 0, count: amt + 1), count: n + 1)
|
||||
// 先頭列を初期化する
|
||||
for i in 0 ... n {
|
||||
dp[i][0] = 1
|
||||
}
|
||||
// 状態遷移
|
||||
for i in 1 ... n {
|
||||
for a in 1 ... amt {
|
||||
if coins[i - 1] > a {
|
||||
// 目標金額を超えるなら硬貨 i は選ばない
|
||||
dp[i][a] = dp[i - 1][a]
|
||||
} else {
|
||||
// コイン i を選ばない場合と選ぶ場合の和
|
||||
dp[i][a] = dp[i - 1][a] + dp[i][a - coins[i - 1]]
|
||||
}
|
||||
}
|
||||
}
|
||||
return dp[n][amt]
|
||||
}
|
||||
|
||||
/* コイン両替 II:空間最適化した動的計画法 */
|
||||
func coinChangeIIDPComp(coins: [Int], amt: Int) -> Int {
|
||||
let n = coins.count
|
||||
// dp テーブルを初期化
|
||||
var dp = Array(repeating: 0, count: amt + 1)
|
||||
dp[0] = 1
|
||||
// 状態遷移
|
||||
for i in 1 ... n {
|
||||
for a in 1 ... amt {
|
||||
if coins[i - 1] > a {
|
||||
// 目標金額を超えるなら硬貨 i は選ばない
|
||||
dp[a] = dp[a]
|
||||
} else {
|
||||
// コイン i を選ばない場合と選ぶ場合の和
|
||||
dp[a] = dp[a] + dp[a - coins[i - 1]]
|
||||
}
|
||||
}
|
||||
}
|
||||
return dp[amt]
|
||||
}
|
||||
|
||||
@main
|
||||
enum CoinChangeII {
|
||||
/* Driver Code */
|
||||
static func main() {
|
||||
let coins = [1, 2, 5]
|
||||
let amt = 5
|
||||
|
||||
// 動的計画法
|
||||
var res = coinChangeIIDP(coins: coins, amt: amt)
|
||||
print("目標金額を作る硬貨の組み合わせ数は \(res)")
|
||||
|
||||
// 空間最適化後の動的計画法
|
||||
res = coinChangeIIDPComp(coins: coins, amt: amt)
|
||||
print("目標金額を作る硬貨の組み合わせ数は \(res)")
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,147 @@
|
||||
/**
|
||||
* File: edit_distance.swift
|
||||
* Created Time: 2023-07-16
|
||||
* Author: nuomi1 (nuomi1@qq.com)
|
||||
*/
|
||||
|
||||
/* 編集距離:総当たり探索 */
|
||||
func editDistanceDFS(s: String, t: String, i: Int, j: Int) -> Int {
|
||||
// s と t がともに空なら 0 を返す
|
||||
if i == 0, j == 0 {
|
||||
return 0
|
||||
}
|
||||
// s が空なら t の長さを返す
|
||||
if i == 0 {
|
||||
return j
|
||||
}
|
||||
// t が空なら s の長さを返す
|
||||
if j == 0 {
|
||||
return i
|
||||
}
|
||||
// 2 つの文字が等しければ、その 2 文字をそのままスキップする
|
||||
if s.utf8CString[i - 1] == t.utf8CString[j - 1] {
|
||||
return editDistanceDFS(s: s, t: t, i: i - 1, j: j - 1)
|
||||
}
|
||||
// 最小編集回数 = 挿入・削除・置換の 3 操作における最小編集回数 + 1
|
||||
let insert = editDistanceDFS(s: s, t: t, i: i, j: j - 1)
|
||||
let delete = editDistanceDFS(s: s, t: t, i: i - 1, j: j)
|
||||
let replace = editDistanceDFS(s: s, t: t, i: i - 1, j: j - 1)
|
||||
// 最小編集回数を返す
|
||||
return min(min(insert, delete), replace) + 1
|
||||
}
|
||||
|
||||
/* 編集距離:メモ化探索 */
|
||||
func editDistanceDFSMem(s: String, t: String, mem: inout [[Int]], i: Int, j: Int) -> Int {
|
||||
// s と t がともに空なら 0 を返す
|
||||
if i == 0, j == 0 {
|
||||
return 0
|
||||
}
|
||||
// s が空なら t の長さを返す
|
||||
if i == 0 {
|
||||
return j
|
||||
}
|
||||
// t が空なら s の長さを返す
|
||||
if j == 0 {
|
||||
return i
|
||||
}
|
||||
// 記録済みなら、それをそのまま返す
|
||||
if mem[i][j] != -1 {
|
||||
return mem[i][j]
|
||||
}
|
||||
// 2 つの文字が等しければ、その 2 文字をそのままスキップする
|
||||
if s.utf8CString[i - 1] == t.utf8CString[j - 1] {
|
||||
return editDistanceDFS(s: s, t: t, i: i - 1, j: j - 1)
|
||||
}
|
||||
// 最小編集回数 = 挿入・削除・置換の 3 操作における最小編集回数 + 1
|
||||
let insert = editDistanceDFS(s: s, t: t, i: i, j: j - 1)
|
||||
let delete = editDistanceDFS(s: s, t: t, i: i - 1, j: j)
|
||||
let replace = editDistanceDFS(s: s, t: t, i: i - 1, j: j - 1)
|
||||
// 最小編集回数を記録して返す
|
||||
mem[i][j] = min(min(insert, delete), replace) + 1
|
||||
return mem[i][j]
|
||||
}
|
||||
|
||||
/* 編集距離:動的計画法 */
|
||||
func editDistanceDP(s: String, t: String) -> Int {
|
||||
let n = s.utf8CString.count
|
||||
let m = t.utf8CString.count
|
||||
var dp = Array(repeating: Array(repeating: 0, count: m + 1), count: n + 1)
|
||||
// 状態遷移:先頭行と先頭列
|
||||
for i in 1 ... n {
|
||||
dp[i][0] = i
|
||||
}
|
||||
for j in 1 ... m {
|
||||
dp[0][j] = j
|
||||
}
|
||||
// 状態遷移: 残りの行と列
|
||||
for i in 1 ... n {
|
||||
for j in 1 ... m {
|
||||
if s.utf8CString[i - 1] == t.utf8CString[j - 1] {
|
||||
// 2 つの文字が等しければ、その 2 文字をそのままスキップする
|
||||
dp[i][j] = dp[i - 1][j - 1]
|
||||
} else {
|
||||
// 最小編集回数 = 挿入・削除・置換の 3 操作における最小編集回数 + 1
|
||||
dp[i][j] = min(min(dp[i][j - 1], dp[i - 1][j]), dp[i - 1][j - 1]) + 1
|
||||
}
|
||||
}
|
||||
}
|
||||
return dp[n][m]
|
||||
}
|
||||
|
||||
/* 編集距離:空間最適化した動的計画法 */
|
||||
func editDistanceDPComp(s: String, t: String) -> Int {
|
||||
let n = s.utf8CString.count
|
||||
let m = t.utf8CString.count
|
||||
var dp = Array(repeating: 0, count: m + 1)
|
||||
// 状態遷移:先頭行
|
||||
for j in 1 ... m {
|
||||
dp[j] = j
|
||||
}
|
||||
// 状態遷移:残りの行
|
||||
for i in 1 ... n {
|
||||
// 状態遷移:先頭列
|
||||
var leftup = dp[0] // dp[i-1, j-1] を一時保存する
|
||||
dp[0] = i
|
||||
// 状態遷移:残りの列
|
||||
for j in 1 ... m {
|
||||
let temp = dp[j]
|
||||
if s.utf8CString[i - 1] == t.utf8CString[j - 1] {
|
||||
// 2 つの文字が等しければ、その 2 文字をそのままスキップする
|
||||
dp[j] = leftup
|
||||
} else {
|
||||
// 最小編集回数 = 挿入・削除・置換の 3 操作における最小編集回数 + 1
|
||||
dp[j] = min(min(dp[j - 1], dp[j]), leftup) + 1
|
||||
}
|
||||
leftup = temp // 次の反復の dp[i-1, j-1] に更新する
|
||||
}
|
||||
}
|
||||
return dp[m]
|
||||
}
|
||||
|
||||
@main
|
||||
enum EditDistance {
|
||||
/* Driver Code */
|
||||
static func main() {
|
||||
let s = "bag"
|
||||
let t = "pack"
|
||||
let n = s.utf8CString.count
|
||||
let m = t.utf8CString.count
|
||||
|
||||
// 全探索
|
||||
var res = editDistanceDFS(s: s, t: t, i: n, j: m)
|
||||
print("\(s) を \(t) に変更するには最小で \(res) 回の編集が必要")
|
||||
|
||||
// メモ化探索
|
||||
var mem = Array(repeating: Array(repeating: -1, count: m + 1), count: n + 1)
|
||||
res = editDistanceDFSMem(s: s, t: t, mem: &mem, i: n, j: m)
|
||||
print("\(s) を \(t) に変更するには最小で \(res) 回の編集が必要")
|
||||
|
||||
// 動的計画法
|
||||
res = editDistanceDP(s: s, t: t)
|
||||
print("\(s) を \(t) に変更するには最小で \(res) 回の編集が必要")
|
||||
|
||||
// 空間最適化後の動的計画法
|
||||
res = editDistanceDPComp(s: s, t: t)
|
||||
print("\(s) を \(t) に変更するには最小で \(res) 回の編集が必要")
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,110 @@
|
||||
/**
|
||||
* File: knapsack.swift
|
||||
* Created Time: 2023-07-15
|
||||
* Author: nuomi1 (nuomi1@qq.com)
|
||||
*/
|
||||
|
||||
/* 0-1 ナップサック:総当たり探索 */
|
||||
func knapsackDFS(wgt: [Int], val: [Int], i: Int, c: Int) -> Int {
|
||||
// すべての品物を選び終えたか、ナップサックに残り容量がなければ、価値 0 を返す
|
||||
if i == 0 || c == 0 {
|
||||
return 0
|
||||
}
|
||||
// ナップサック容量を超える場合は、入れない選択しかできない
|
||||
if wgt[i - 1] > c {
|
||||
return knapsackDFS(wgt: wgt, val: val, i: i - 1, c: c)
|
||||
}
|
||||
// 品物 i を入れない場合と入れる場合の最大価値を計算する
|
||||
let no = knapsackDFS(wgt: wgt, val: val, i: i - 1, c: c)
|
||||
let yes = knapsackDFS(wgt: wgt, val: val, i: i - 1, c: c - wgt[i - 1]) + val[i - 1]
|
||||
// 2つの案のうち価値が大きいほうを返す
|
||||
return max(no, yes)
|
||||
}
|
||||
|
||||
/* 0-1 ナップサック:メモ化探索 */
|
||||
func knapsackDFSMem(wgt: [Int], val: [Int], mem: inout [[Int]], i: Int, c: Int) -> Int {
|
||||
// すべての品物を選び終えたか、ナップサックに残り容量がなければ、価値 0 を返す
|
||||
if i == 0 || c == 0 {
|
||||
return 0
|
||||
}
|
||||
// 既に記録があればそのまま返す
|
||||
if mem[i][c] != -1 {
|
||||
return mem[i][c]
|
||||
}
|
||||
// ナップサック容量を超える場合は、入れない選択しかできない
|
||||
if wgt[i - 1] > c {
|
||||
return knapsackDFSMem(wgt: wgt, val: val, mem: &mem, i: i - 1, c: c)
|
||||
}
|
||||
// 品物 i を入れない場合と入れる場合の最大価値を計算する
|
||||
let no = knapsackDFSMem(wgt: wgt, val: val, mem: &mem, i: i - 1, c: c)
|
||||
let yes = knapsackDFSMem(wgt: wgt, val: val, mem: &mem, i: i - 1, c: c - wgt[i - 1]) + val[i - 1]
|
||||
// 2 つの案のうち価値が大きい方を記録して返す
|
||||
mem[i][c] = max(no, yes)
|
||||
return mem[i][c]
|
||||
}
|
||||
|
||||
/* 0-1 ナップサック:動的計画法 */
|
||||
func knapsackDP(wgt: [Int], val: [Int], cap: Int) -> Int {
|
||||
let n = wgt.count
|
||||
// dp テーブルを初期化
|
||||
var dp = Array(repeating: Array(repeating: 0, count: cap + 1), count: n + 1)
|
||||
// 状態遷移
|
||||
for i in 1 ... n {
|
||||
for c in 1 ... cap {
|
||||
if wgt[i - 1] > c {
|
||||
// ナップサック容量を超えるなら品物 i は選ばない
|
||||
dp[i][c] = dp[i - 1][c]
|
||||
} else {
|
||||
// 品物 i を選ばない場合と選ぶ場合の大きい方
|
||||
dp[i][c] = max(dp[i - 1][c], dp[i - 1][c - wgt[i - 1]] + val[i - 1])
|
||||
}
|
||||
}
|
||||
}
|
||||
return dp[n][cap]
|
||||
}
|
||||
|
||||
/* 0-1 ナップサック:空間最適化後の動的計画法 */
|
||||
func knapsackDPComp(wgt: [Int], val: [Int], cap: Int) -> Int {
|
||||
let n = wgt.count
|
||||
// dp テーブルを初期化
|
||||
var dp = Array(repeating: 0, count: cap + 1)
|
||||
// 状態遷移
|
||||
for i in 1 ... n {
|
||||
// 逆順に走査する
|
||||
for c in (1 ... cap).reversed() {
|
||||
if wgt[i - 1] <= c {
|
||||
// 品物 i を選ばない場合と選ぶ場合の大きい方
|
||||
dp[c] = max(dp[c], dp[c - wgt[i - 1]] + val[i - 1])
|
||||
}
|
||||
}
|
||||
}
|
||||
return dp[cap]
|
||||
}
|
||||
|
||||
@main
|
||||
enum Knapsack {
|
||||
/* Driver Code */
|
||||
static func main() {
|
||||
let wgt = [10, 20, 30, 40, 50]
|
||||
let val = [50, 120, 150, 210, 240]
|
||||
let cap = 50
|
||||
let n = wgt.count
|
||||
|
||||
// 全探索
|
||||
var res = knapsackDFS(wgt: wgt, val: val, i: n, c: cap)
|
||||
print("ナップサック容量を超えない最大価値は \(res)")
|
||||
|
||||
// メモ化探索
|
||||
var mem = Array(repeating: Array(repeating: -1, count: cap + 1), count: n + 1)
|
||||
res = knapsackDFSMem(wgt: wgt, val: val, mem: &mem, i: n, c: cap)
|
||||
print("ナップサック容量を超えない最大価値は \(res)")
|
||||
|
||||
// 動的計画法
|
||||
res = knapsackDP(wgt: wgt, val: val, cap: cap)
|
||||
print("ナップサック容量を超えない最大価値は \(res)")
|
||||
|
||||
// 空間最適化後の動的計画法
|
||||
res = knapsackDPComp(wgt: wgt, val: val, cap: cap)
|
||||
print("ナップサック容量を超えない最大価値は \(res)")
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
/**
|
||||
* File: min_cost_climbing_stairs_dp.swift
|
||||
* Created Time: 2023-07-15
|
||||
* Author: nuomi1 (nuomi1@qq.com)
|
||||
*/
|
||||
|
||||
/* 階段登りの最小コスト:動的計画法 */
|
||||
func minCostClimbingStairsDP(cost: [Int]) -> Int {
|
||||
let n = cost.count - 1
|
||||
if n == 1 || n == 2 {
|
||||
return cost[n]
|
||||
}
|
||||
// 部分問題の解を保存するために dp テーブルを初期化
|
||||
var dp = Array(repeating: 0, count: n + 1)
|
||||
// 初期状態:最小部分問題の解をあらかじめ設定
|
||||
dp[1] = cost[1]
|
||||
dp[2] = cost[2]
|
||||
// 状態遷移:小さい部分問題から大きい部分問題へ順に解く
|
||||
for i in 3 ... n {
|
||||
dp[i] = min(dp[i - 1], dp[i - 2]) + cost[i]
|
||||
}
|
||||
return dp[n]
|
||||
}
|
||||
|
||||
/* 階段昇りの最小コスト:空間最適化後の動的計画法 */
|
||||
func minCostClimbingStairsDPComp(cost: [Int]) -> Int {
|
||||
let n = cost.count - 1
|
||||
if n == 1 || n == 2 {
|
||||
return cost[n]
|
||||
}
|
||||
var (a, b) = (cost[1], cost[2])
|
||||
for i in 3 ... n {
|
||||
(a, b) = (b, min(a, b) + cost[i])
|
||||
}
|
||||
return b
|
||||
}
|
||||
|
||||
@main
|
||||
enum MinCostClimbingStairsDP {
|
||||
/* Driver Code */
|
||||
static func main() {
|
||||
let cost = [0, 1, 10, 1, 1, 1, 10, 1, 1, 10, 1]
|
||||
print("入力された階段のコスト一覧は \(cost)")
|
||||
|
||||
var res = minCostClimbingStairsDP(cost: cost)
|
||||
print("階段を上り切る最小コストは \(res)")
|
||||
|
||||
res = minCostClimbingStairsDPComp(cost: cost)
|
||||
print("階段を上り切る最小コストは \(res)")
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,123 @@
|
||||
/**
|
||||
* File: min_path_sum.swift
|
||||
* Created Time: 2023-07-15
|
||||
* Author: nuomi1 (nuomi1@qq.com)
|
||||
*/
|
||||
|
||||
/* 最小経路和:全探索 */
|
||||
func minPathSumDFS(grid: [[Int]], i: Int, j: Int) -> Int {
|
||||
// 左上のセルなら探索を終了する
|
||||
if i == 0, j == 0 {
|
||||
return grid[0][0]
|
||||
}
|
||||
// 行または列のインデックスが範囲外なら、コスト +∞ を返す
|
||||
if i < 0 || j < 0 {
|
||||
return .max
|
||||
}
|
||||
// 左上から (i-1, j) および (i, j-1) までの最小経路コストを計算する
|
||||
let up = minPathSumDFS(grid: grid, i: i - 1, j: j)
|
||||
let left = minPathSumDFS(grid: grid, i: i, j: j - 1)
|
||||
// 左上隅から (i, j) までの最小経路コストを返す
|
||||
return min(left, up) + grid[i][j]
|
||||
}
|
||||
|
||||
/* 最小経路和:メモ化探索 */
|
||||
func minPathSumDFSMem(grid: [[Int]], mem: inout [[Int]], i: Int, j: Int) -> Int {
|
||||
// 左上のセルなら探索を終了する
|
||||
if i == 0, j == 0 {
|
||||
return grid[0][0]
|
||||
}
|
||||
// 行または列のインデックスが範囲外なら、コスト +∞ を返す
|
||||
if i < 0 || j < 0 {
|
||||
return .max
|
||||
}
|
||||
// 既に記録があればそのまま返す
|
||||
if mem[i][j] != -1 {
|
||||
return mem[i][j]
|
||||
}
|
||||
// 左と上のセルからの最小経路コスト
|
||||
let up = minPathSumDFSMem(grid: grid, mem: &mem, i: i - 1, j: j)
|
||||
let left = minPathSumDFSMem(grid: grid, mem: &mem, i: i, j: j - 1)
|
||||
// 左上から (i, j) までの最小経路コストを記録して返す
|
||||
mem[i][j] = min(left, up) + grid[i][j]
|
||||
return mem[i][j]
|
||||
}
|
||||
|
||||
/* 最小経路和:動的計画法 */
|
||||
func minPathSumDP(grid: [[Int]]) -> Int {
|
||||
let n = grid.count
|
||||
let m = grid[0].count
|
||||
// dp テーブルを初期化
|
||||
var dp = Array(repeating: Array(repeating: 0, count: m), count: n)
|
||||
dp[0][0] = grid[0][0]
|
||||
// 状態遷移:先頭行
|
||||
for j in 1 ..< m {
|
||||
dp[0][j] = dp[0][j - 1] + grid[0][j]
|
||||
}
|
||||
// 状態遷移:先頭列
|
||||
for i in 1 ..< n {
|
||||
dp[i][0] = dp[i - 1][0] + grid[i][0]
|
||||
}
|
||||
// 状態遷移: 残りの行と列
|
||||
for i in 1 ..< n {
|
||||
for j in 1 ..< m {
|
||||
dp[i][j] = min(dp[i][j - 1], dp[i - 1][j]) + grid[i][j]
|
||||
}
|
||||
}
|
||||
return dp[n - 1][m - 1]
|
||||
}
|
||||
|
||||
/* 最小経路和:空間最適化後の動的計画法 */
|
||||
func minPathSumDPComp(grid: [[Int]]) -> Int {
|
||||
let n = grid.count
|
||||
let m = grid[0].count
|
||||
// dp テーブルを初期化
|
||||
var dp = Array(repeating: 0, count: m)
|
||||
// 状態遷移:先頭行
|
||||
dp[0] = grid[0][0]
|
||||
for j in 1 ..< m {
|
||||
dp[j] = dp[j - 1] + grid[0][j]
|
||||
}
|
||||
// 状態遷移:残りの行
|
||||
for i in 1 ..< n {
|
||||
// 状態遷移:先頭列
|
||||
dp[0] = dp[0] + grid[i][0]
|
||||
// 状態遷移:残りの列
|
||||
for j in 1 ..< m {
|
||||
dp[j] = min(dp[j - 1], dp[j]) + grid[i][j]
|
||||
}
|
||||
}
|
||||
return dp[m - 1]
|
||||
}
|
||||
|
||||
@main
|
||||
enum MinPathSum {
|
||||
/* Driver Code */
|
||||
static func main() {
|
||||
let grid = [
|
||||
[1, 3, 1, 5],
|
||||
[2, 2, 4, 2],
|
||||
[5, 3, 2, 1],
|
||||
[4, 3, 5, 2],
|
||||
]
|
||||
let n = grid.count
|
||||
let m = grid[0].count
|
||||
|
||||
// 全探索
|
||||
var res = minPathSumDFS(grid: grid, i: n - 1, j: m - 1)
|
||||
print("左上から右下までの最小経路和は \(res)")
|
||||
|
||||
// メモ化探索
|
||||
var mem = Array(repeating: Array(repeating: -1, count: m), count: n)
|
||||
res = minPathSumDFSMem(grid: grid, mem: &mem, i: n - 1, j: m - 1)
|
||||
print("左上から右下までの最小経路和は \(res)")
|
||||
|
||||
// 動的計画法
|
||||
res = minPathSumDP(grid: grid)
|
||||
print("左上から右下までの最小経路和は \(res)")
|
||||
|
||||
// 空間最適化後の動的計画法
|
||||
res = minPathSumDPComp(grid: grid)
|
||||
print("左上から右下までの最小経路和は \(res)")
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
/**
|
||||
* File: unbounded_knapsack.swift
|
||||
* Created Time: 2023-07-15
|
||||
* Author: nuomi1 (nuomi1@qq.com)
|
||||
*/
|
||||
|
||||
/* 完全ナップサック問題:動的計画法 */
|
||||
func unboundedKnapsackDP(wgt: [Int], val: [Int], cap: Int) -> Int {
|
||||
let n = wgt.count
|
||||
// dp テーブルを初期化
|
||||
var dp = Array(repeating: Array(repeating: 0, count: cap + 1), count: n + 1)
|
||||
// 状態遷移
|
||||
for i in 1 ... n {
|
||||
for c in 1 ... cap {
|
||||
if wgt[i - 1] > c {
|
||||
// ナップサック容量を超えるなら品物 i は選ばない
|
||||
dp[i][c] = dp[i - 1][c]
|
||||
} else {
|
||||
// 品物 i を選ばない場合と選ぶ場合の大きい方
|
||||
dp[i][c] = max(dp[i - 1][c], dp[i][c - wgt[i - 1]] + val[i - 1])
|
||||
}
|
||||
}
|
||||
}
|
||||
return dp[n][cap]
|
||||
}
|
||||
|
||||
/* 完全ナップサック問題:空間最適化後の動的計画法 */
|
||||
func unboundedKnapsackDPComp(wgt: [Int], val: [Int], cap: Int) -> Int {
|
||||
let n = wgt.count
|
||||
// dp テーブルを初期化
|
||||
var dp = Array(repeating: 0, count: cap + 1)
|
||||
// 状態遷移
|
||||
for i in 1 ... n {
|
||||
for c in 1 ... cap {
|
||||
if wgt[i - 1] > c {
|
||||
// ナップサック容量を超えるなら品物 i は選ばない
|
||||
dp[c] = dp[c]
|
||||
} else {
|
||||
// 品物 i を選ばない場合と選ぶ場合の大きい方
|
||||
dp[c] = max(dp[c], dp[c - wgt[i - 1]] + val[i - 1])
|
||||
}
|
||||
}
|
||||
}
|
||||
return dp[cap]
|
||||
}
|
||||
|
||||
@main
|
||||
enum UnboundedKnapsack {
|
||||
/* Driver Code */
|
||||
static func main() {
|
||||
let wgt = [1, 2, 3]
|
||||
let val = [5, 11, 15]
|
||||
let cap = 4
|
||||
|
||||
// 動的計画法
|
||||
var res = unboundedKnapsackDP(wgt: wgt, val: val, cap: cap)
|
||||
print("ナップサック容量を超えない最大価値は \(res)")
|
||||
|
||||
// 空間最適化後の動的計画法
|
||||
res = unboundedKnapsackDPComp(wgt: wgt, val: val, cap: cap)
|
||||
print("ナップサック容量を超えない最大価値は \(res)")
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,121 @@
|
||||
/**
|
||||
* File: graph_adjacency_list.swift
|
||||
* Created Time: 2023-02-01
|
||||
* Author: nuomi1 (nuomi1@qq.com)
|
||||
*/
|
||||
|
||||
import utils
|
||||
|
||||
/* 隣接リストに基づく無向グラフクラス */
|
||||
public class GraphAdjList {
|
||||
// 隣接リスト。key は頂点、value はその頂点に隣接する全頂点
|
||||
public private(set) var adjList: [Vertex: [Vertex]]
|
||||
|
||||
/* コンストラクタ */
|
||||
public init(edges: [[Vertex]]) {
|
||||
adjList = [:]
|
||||
// すべての頂点と辺を追加
|
||||
for edge in edges {
|
||||
addVertex(vet: edge[0])
|
||||
addVertex(vet: edge[1])
|
||||
addEdge(vet1: edge[0], vet2: edge[1])
|
||||
}
|
||||
}
|
||||
|
||||
/* 頂点数を取得 */
|
||||
public func size() -> Int {
|
||||
adjList.count
|
||||
}
|
||||
|
||||
/* 辺を追加 */
|
||||
public func addEdge(vet1: Vertex, vet2: Vertex) {
|
||||
if adjList[vet1] == nil || adjList[vet2] == nil || vet1 == vet2 {
|
||||
fatalError("引数エラー")
|
||||
}
|
||||
// 辺 vet1 - vet2 を追加
|
||||
adjList[vet1]?.append(vet2)
|
||||
adjList[vet2]?.append(vet1)
|
||||
}
|
||||
|
||||
/* 辺を削除 */
|
||||
public func removeEdge(vet1: Vertex, vet2: Vertex) {
|
||||
if adjList[vet1] == nil || adjList[vet2] == nil || vet1 == vet2 {
|
||||
fatalError("引数エラー")
|
||||
}
|
||||
// 辺 vet1 - vet2 を削除
|
||||
adjList[vet1]?.removeAll { $0 == vet2 }
|
||||
adjList[vet2]?.removeAll { $0 == vet1 }
|
||||
}
|
||||
|
||||
/* 頂点を追加 */
|
||||
public func addVertex(vet: Vertex) {
|
||||
if adjList[vet] != nil {
|
||||
return
|
||||
}
|
||||
// 隣接リストに新しいリストを追加
|
||||
adjList[vet] = []
|
||||
}
|
||||
|
||||
/* 頂点を削除 */
|
||||
public func removeVertex(vet: Vertex) {
|
||||
if adjList[vet] == nil {
|
||||
fatalError("引数エラー")
|
||||
}
|
||||
// 隣接リストから頂点 vet に対応するリストを削除
|
||||
adjList.removeValue(forKey: vet)
|
||||
// 他の頂点のリストを走査し、vet を含むすべての辺を削除
|
||||
for key in adjList.keys {
|
||||
adjList[key]?.removeAll { $0 == vet }
|
||||
}
|
||||
}
|
||||
|
||||
/* 隣接リストを出力 */
|
||||
public func print() {
|
||||
Swift.print("隣接リスト =")
|
||||
for (vertex, list) in adjList {
|
||||
let list = list.map { $0.val }
|
||||
Swift.print("\(vertex.val): \(list),")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#if !TARGET
|
||||
|
||||
@main
|
||||
enum GraphAdjacencyList {
|
||||
/* Driver Code */
|
||||
static func main() {
|
||||
/* 無向グラフを初期化 */
|
||||
let v = Vertex.valsToVets(vals: [1, 3, 2, 5, 4])
|
||||
let edges = [[v[0], v[1]], [v[0], v[3]], [v[1], v[2]], [v[2], v[3]], [v[2], v[4]], [v[3], v[4]]]
|
||||
let graph = GraphAdjList(edges: edges)
|
||||
print("\n初期化後のグラフ")
|
||||
graph.print()
|
||||
|
||||
/* 辺を追加 */
|
||||
// 頂点 1, 2 は v[0], v[2]
|
||||
graph.addEdge(vet1: v[0], vet2: v[2])
|
||||
print("\n辺 1-2 を追加後のグラフ")
|
||||
graph.print()
|
||||
|
||||
/* 辺を削除 */
|
||||
// 頂点 1, 3 は v[0], v[1]
|
||||
graph.removeEdge(vet1: v[0], vet2: v[1])
|
||||
print("\n辺 1-3 を削除後のグラフ")
|
||||
graph.print()
|
||||
|
||||
/* 頂点を追加 */
|
||||
let v5 = Vertex(val: 6)
|
||||
graph.addVertex(vet: v5)
|
||||
print("\n頂点 6 を追加後のグラフ")
|
||||
graph.print()
|
||||
|
||||
/* 頂点を削除 */
|
||||
// 頂点 3 は v[1]
|
||||
graph.removeVertex(vet: v[1])
|
||||
print("\n頂点 3 を削除後のグラフ")
|
||||
graph.print()
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,121 @@
|
||||
/**
|
||||
* File: graph_adjacency_list.swift
|
||||
* Created Time: 2023-02-01
|
||||
* Author: nuomi1 (nuomi1@qq.com)
|
||||
*/
|
||||
|
||||
import utils
|
||||
|
||||
/* 隣接リストに基づく無向グラフクラス */
|
||||
public class GraphAdjList {
|
||||
// 隣接リスト。key は頂点、value はその頂点に隣接する全頂点
|
||||
public private(set) var adjList: [Vertex: [Vertex]]
|
||||
|
||||
/* コンストラクタ */
|
||||
public init(edges: [[Vertex]]) {
|
||||
adjList = [:]
|
||||
// すべての頂点と辺を追加
|
||||
for edge in edges {
|
||||
addVertex(vet: edge[0])
|
||||
addVertex(vet: edge[1])
|
||||
addEdge(vet1: edge[0], vet2: edge[1])
|
||||
}
|
||||
}
|
||||
|
||||
/* 頂点数を取得 */
|
||||
public func size() -> Int {
|
||||
adjList.count
|
||||
}
|
||||
|
||||
/* 辺を追加 */
|
||||
public func addEdge(vet1: Vertex, vet2: Vertex) {
|
||||
if adjList[vet1] == nil || adjList[vet2] == nil || vet1 == vet2 {
|
||||
fatalError("引数エラー")
|
||||
}
|
||||
// 辺 vet1 - vet2 を追加
|
||||
adjList[vet1]?.append(vet2)
|
||||
adjList[vet2]?.append(vet1)
|
||||
}
|
||||
|
||||
/* 辺を削除 */
|
||||
public func removeEdge(vet1: Vertex, vet2: Vertex) {
|
||||
if adjList[vet1] == nil || adjList[vet2] == nil || vet1 == vet2 {
|
||||
fatalError("引数エラー")
|
||||
}
|
||||
// 辺 vet1 - vet2 を削除
|
||||
adjList[vet1]?.removeAll { $0 == vet2 }
|
||||
adjList[vet2]?.removeAll { $0 == vet1 }
|
||||
}
|
||||
|
||||
/* 頂点を追加 */
|
||||
public func addVertex(vet: Vertex) {
|
||||
if adjList[vet] != nil {
|
||||
return
|
||||
}
|
||||
// 隣接リストに新しいリストを追加
|
||||
adjList[vet] = []
|
||||
}
|
||||
|
||||
/* 頂点を削除 */
|
||||
public func removeVertex(vet: Vertex) {
|
||||
if adjList[vet] == nil {
|
||||
fatalError("引数エラー")
|
||||
}
|
||||
// 隣接リストから頂点 vet に対応するリストを削除
|
||||
adjList.removeValue(forKey: vet)
|
||||
// 他の頂点のリストを走査し、vet を含むすべての辺を削除
|
||||
for key in adjList.keys {
|
||||
adjList[key]?.removeAll { $0 == vet }
|
||||
}
|
||||
}
|
||||
|
||||
/* 隣接リストを出力 */
|
||||
public func print() {
|
||||
Swift.print("隣接リスト =")
|
||||
for (vertex, list) in adjList {
|
||||
let list = list.map { $0.val }
|
||||
Swift.print("\(vertex.val): \(list),")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#if !TARGET
|
||||
|
||||
@main
|
||||
enum GraphAdjacencyList {
|
||||
/* Driver Code */
|
||||
static func main() {
|
||||
/* 無向グラフを初期化 */
|
||||
let v = Vertex.valsToVets(vals: [1, 3, 2, 5, 4])
|
||||
let edges = [[v[0], v[1]], [v[0], v[3]], [v[1], v[2]], [v[2], v[3]], [v[2], v[4]], [v[3], v[4]]]
|
||||
let graph = GraphAdjList(edges: edges)
|
||||
print("\n初期化後のグラフ")
|
||||
graph.print()
|
||||
|
||||
/* 辺を追加 */
|
||||
// 頂点 1, 2 は v[0], v[2]
|
||||
graph.addEdge(vet1: v[0], vet2: v[2])
|
||||
print("\n辺 1-2 を追加後のグラフ")
|
||||
graph.print()
|
||||
|
||||
/* 辺を削除 */
|
||||
// 頂点 1, 3 は v[0], v[1]
|
||||
graph.removeEdge(vet1: v[0], vet2: v[1])
|
||||
print("\n辺 1-3 を削除後のグラフ")
|
||||
graph.print()
|
||||
|
||||
/* 頂点を追加 */
|
||||
let v5 = Vertex(val: 6)
|
||||
graph.addVertex(vet: v5)
|
||||
print("\n頂点 6 を追加後のグラフ")
|
||||
graph.print()
|
||||
|
||||
/* 頂点を削除 */
|
||||
// 頂点 3 は v[1]
|
||||
graph.removeVertex(vet: v[1])
|
||||
print("\n頂点 3 を削除後のグラフ")
|
||||
graph.print()
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,130 @@
|
||||
/**
|
||||
* File: graph_adjacency_matrix.swift
|
||||
* Created Time: 2023-02-01
|
||||
* Author: nuomi1 (nuomi1@qq.com)
|
||||
*/
|
||||
|
||||
import utils
|
||||
|
||||
/* 隣接行列に基づく無向グラフクラス */
|
||||
class GraphAdjMat {
|
||||
private var vertices: [Int] // 頂点リスト。要素は「頂点値」、インデックスは「頂点インデックス」を表す
|
||||
private var adjMat: [[Int]] // 隣接行列。行・列のインデックスは「頂点インデックス」に対応
|
||||
|
||||
/* コンストラクタ */
|
||||
init(vertices: [Int], edges: [[Int]]) {
|
||||
self.vertices = []
|
||||
adjMat = []
|
||||
// 頂点を追加
|
||||
for val in vertices {
|
||||
addVertex(val: val)
|
||||
}
|
||||
// 辺を追加
|
||||
// 注意:edges の各要素は頂点インデックスを表し、vertices の要素インデックスに対応する
|
||||
for e in edges {
|
||||
addEdge(i: e[0], j: e[1])
|
||||
}
|
||||
}
|
||||
|
||||
/* 頂点数を取得 */
|
||||
func size() -> Int {
|
||||
vertices.count
|
||||
}
|
||||
|
||||
/* 頂点を追加 */
|
||||
func addVertex(val: Int) {
|
||||
let n = size()
|
||||
// 頂点リストに新しい頂点の値を追加
|
||||
vertices.append(val)
|
||||
// 隣接行列に 1 行追加
|
||||
let newRow = Array(repeating: 0, count: n)
|
||||
adjMat.append(newRow)
|
||||
// 隣接行列に 1 列追加
|
||||
for i in adjMat.indices {
|
||||
adjMat[i].append(0)
|
||||
}
|
||||
}
|
||||
|
||||
/* 頂点を削除 */
|
||||
func removeVertex(index: Int) {
|
||||
if index >= size() {
|
||||
fatalError("範囲外")
|
||||
}
|
||||
// 頂点リストから index の頂点を削除する
|
||||
vertices.remove(at: index)
|
||||
// 隣接行列で index 行を削除する
|
||||
adjMat.remove(at: index)
|
||||
// 隣接行列で index 列を削除する
|
||||
for i in adjMat.indices {
|
||||
adjMat[i].remove(at: index)
|
||||
}
|
||||
}
|
||||
|
||||
/* 辺を追加 */
|
||||
// 引数 i, j は vertices の要素インデックスに対応する
|
||||
func addEdge(i: Int, j: Int) {
|
||||
// インデックスの範囲外と等値の処理
|
||||
if i < 0 || j < 0 || i >= size() || j >= size() || i == j {
|
||||
fatalError("範囲外")
|
||||
}
|
||||
// 無向グラフでは、隣接行列は主対角線に関して対称、すなわち (i, j) == (j, i) を満たす
|
||||
adjMat[i][j] = 1
|
||||
adjMat[j][i] = 1
|
||||
}
|
||||
|
||||
/* 辺を削除 */
|
||||
// 引数 i, j は vertices の要素インデックスに対応する
|
||||
func removeEdge(i: Int, j: Int) {
|
||||
// インデックスの範囲外と等値の処理
|
||||
if i < 0 || j < 0 || i >= size() || j >= size() || i == j {
|
||||
fatalError("範囲外")
|
||||
}
|
||||
adjMat[i][j] = 0
|
||||
adjMat[j][i] = 0
|
||||
}
|
||||
|
||||
/* 隣接行列を出力 */
|
||||
func print() {
|
||||
Swift.print("頂点リスト = ", terminator: "")
|
||||
Swift.print(vertices)
|
||||
Swift.print("隣接行列 =")
|
||||
PrintUtil.printMatrix(matrix: adjMat)
|
||||
}
|
||||
}
|
||||
|
||||
@main
|
||||
enum GraphAdjacencyMatrix {
|
||||
/* Driver Code */
|
||||
static func main() {
|
||||
/* 無向グラフを初期化 */
|
||||
// edges の要素は頂点インデックス、すなわち vertices の要素インデックスに対応する点に注意
|
||||
let vertices = [1, 3, 2, 5, 4]
|
||||
let edges = [[0, 1], [1, 2], [2, 3], [0, 3], [2, 4], [3, 4]]
|
||||
let graph = GraphAdjMat(vertices: vertices, edges: edges)
|
||||
print("\n初期化後のグラフ")
|
||||
graph.print()
|
||||
|
||||
/* 辺を追加 */
|
||||
// 頂点 1, 2 のインデックスはそれぞれ 0, 2
|
||||
graph.addEdge(i: 0, j: 2)
|
||||
print("\n辺 1-2 を追加後のグラフ")
|
||||
graph.print()
|
||||
|
||||
/* 辺を削除 */
|
||||
// 頂点 1, 3 のインデックスはそれぞれ 0, 1
|
||||
graph.removeEdge(i: 0, j: 1)
|
||||
print("\n辺 1-3 を削除後のグラフ")
|
||||
graph.print()
|
||||
|
||||
/* 頂点を追加 */
|
||||
graph.addVertex(val: 6)
|
||||
print("\n頂点 6 を追加後のグラフ")
|
||||
graph.print()
|
||||
|
||||
/* 頂点を削除 */
|
||||
// 頂点 3 のインデックスは 1
|
||||
graph.removeVertex(index: 1)
|
||||
print("\n頂点 3 を削除後のグラフ")
|
||||
graph.print()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
/**
|
||||
* File: graph_bfs.swift
|
||||
* Created Time: 2023-02-21
|
||||
* Author: nuomi1 (nuomi1@qq.com)
|
||||
*/
|
||||
|
||||
import graph_adjacency_list_target
|
||||
import utils
|
||||
|
||||
/* 幅優先探索 */
|
||||
// グラフを隣接リストで表し、指定した頂点の隣接頂点をすべて取得できるようにする
|
||||
func graphBFS(graph: GraphAdjList, startVet: Vertex) -> [Vertex] {
|
||||
// 頂点の走査順序
|
||||
var res: [Vertex] = []
|
||||
// 訪問済み頂点を記録するためのハッシュ集合
|
||||
var visited: Set<Vertex> = [startVet]
|
||||
// BFS の実装にキューを用いる
|
||||
var que: [Vertex] = [startVet]
|
||||
// 頂点 vet を起点に、すべての頂点を訪問し終えるまで繰り返す
|
||||
while !que.isEmpty {
|
||||
let vet = que.removeFirst() // 先頭の頂点をデキュー
|
||||
res.append(vet) // 訪問した頂点を記録
|
||||
// この頂点のすべての隣接頂点を走査
|
||||
for adjVet in graph.adjList[vet] ?? [] {
|
||||
if visited.contains(adjVet) {
|
||||
continue // 訪問済みの頂点をスキップ
|
||||
}
|
||||
que.append(adjVet) // 未訪問の頂点のみをキューに追加
|
||||
visited.insert(adjVet) // この頂点を訪問済みにする
|
||||
}
|
||||
}
|
||||
// 頂点の走査順を返す
|
||||
return res
|
||||
}
|
||||
|
||||
@main
|
||||
enum GraphBFS {
|
||||
/* Driver Code */
|
||||
static func main() {
|
||||
/* 無向グラフを初期化 */
|
||||
let v = Vertex.valsToVets(vals: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9])
|
||||
let edges = [
|
||||
[v[0], v[1]], [v[0], v[3]], [v[1], v[2]], [v[1], v[4]],
|
||||
[v[2], v[5]], [v[3], v[4]], [v[3], v[6]], [v[4], v[5]],
|
||||
[v[4], v[7]], [v[5], v[8]], [v[6], v[7]], [v[7], v[8]],
|
||||
]
|
||||
let graph = GraphAdjList(edges: edges)
|
||||
print("\n初期化後のグラフ")
|
||||
graph.print()
|
||||
|
||||
/* 幅優先探索 */
|
||||
let res = graphBFS(graph: graph, startVet: v[0])
|
||||
print("\n幅優先探索(BFS)の頂点列")
|
||||
print(Vertex.vetsToVals(vets: res))
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
/**
|
||||
* File: graph_dfs.swift
|
||||
* Created Time: 2023-02-21
|
||||
* Author: nuomi1 (nuomi1@qq.com)
|
||||
*/
|
||||
|
||||
import graph_adjacency_list_target
|
||||
import utils
|
||||
|
||||
/* 深さ優先走査の補助関数 */
|
||||
func dfs(graph: GraphAdjList, visited: inout Set<Vertex>, res: inout [Vertex], vet: Vertex) {
|
||||
res.append(vet) // 訪問した頂点を記録
|
||||
visited.insert(vet) // この頂点を訪問済みにする
|
||||
// この頂点のすべての隣接頂点を走査
|
||||
for adjVet in graph.adjList[vet] ?? [] {
|
||||
if visited.contains(adjVet) {
|
||||
continue // 訪問済みの頂点をスキップ
|
||||
}
|
||||
// 隣接頂点を再帰的に訪問
|
||||
dfs(graph: graph, visited: &visited, res: &res, vet: adjVet)
|
||||
}
|
||||
}
|
||||
|
||||
/* 深さ優先探索 */
|
||||
// グラフを隣接リストで表し、指定した頂点の隣接頂点をすべて取得できるようにする
|
||||
func graphDFS(graph: GraphAdjList, startVet: Vertex) -> [Vertex] {
|
||||
// 頂点の走査順序
|
||||
var res: [Vertex] = []
|
||||
// 訪問済み頂点を記録するためのハッシュ集合
|
||||
var visited: Set<Vertex> = []
|
||||
dfs(graph: graph, visited: &visited, res: &res, vet: startVet)
|
||||
return res
|
||||
}
|
||||
|
||||
@main
|
||||
enum GraphDFS {
|
||||
/* Driver Code */
|
||||
static func main() {
|
||||
/* 無向グラフを初期化 */
|
||||
let v = Vertex.valsToVets(vals: [0, 1, 2, 3, 4, 5, 6])
|
||||
let edges = [
|
||||
[v[0], v[1]], [v[0], v[3]], [v[1], v[2]],
|
||||
[v[2], v[5]], [v[4], v[5]], [v[5], v[6]],
|
||||
]
|
||||
let graph = GraphAdjList(edges: edges)
|
||||
print("\n初期化後のグラフ")
|
||||
graph.print()
|
||||
|
||||
/* 深さ優先探索 */
|
||||
let res = graphDFS(graph: graph, startVet: v[0])
|
||||
print("\n深さ優先探索(DFS)の頂点列")
|
||||
print(Vertex.vetsToVals(vets: res))
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
/**
|
||||
* File: coin_change_greedy.swift
|
||||
* Created Time: 2023-09-03
|
||||
* Author: nuomi1 (nuomi1@qq.com)
|
||||
*/
|
||||
|
||||
/* コイン交換:貪欲法 */
|
||||
func coinChangeGreedy(coins: [Int], amt: Int) -> Int {
|
||||
// coins リストはソート済みと仮定する
|
||||
var i = coins.count - 1
|
||||
var count = 0
|
||||
var amt = amt
|
||||
// 残額がなくなるまで貪欲選択を繰り返す
|
||||
while amt > 0 {
|
||||
// 残額以下で最も近い硬貨を見つける
|
||||
while i > 0 && coins[i] > amt {
|
||||
i -= 1
|
||||
}
|
||||
// coins[i] を選択する
|
||||
amt -= coins[i]
|
||||
count += 1
|
||||
}
|
||||
// 実行可能な解が見つからなければ -1 を返す
|
||||
return amt == 0 ? count : -1
|
||||
}
|
||||
|
||||
@main
|
||||
enum CoinChangeGreedy {
|
||||
/* Driver Code */
|
||||
static func main() {
|
||||
// 貪欲法:大域最適解を保証できる
|
||||
var coins = [1, 5, 10, 20, 50, 100]
|
||||
var amt = 186
|
||||
var res = coinChangeGreedy(coins: coins, amt: amt)
|
||||
print("\ncoins = \(coins), amount = \(amt)")
|
||||
print("\(amt) を作るのに必要な最小硬貨枚数は \(res)")
|
||||
|
||||
// 貪欲法:大域最適解を保証できない
|
||||
coins = [1, 20, 50]
|
||||
amt = 60
|
||||
res = coinChangeGreedy(coins: coins, amt: amt)
|
||||
print("\ncoins = \(coins), amount = \(amt)")
|
||||
print("\(amt) を作るのに必要な最小硬貨枚数は \(res)")
|
||||
print("実際に必要な最小枚数は 3、つまり 20 + 20 + 20")
|
||||
|
||||
// 貪欲法:大域最適解を保証できない
|
||||
coins = [1, 49, 50]
|
||||
amt = 98
|
||||
res = coinChangeGreedy(coins: coins, amt: amt)
|
||||
print("\ncoins = \(coins), amount = \(amt)")
|
||||
print("\(amt) を作るのに必要な最小硬貨枚数は \(res)")
|
||||
print("実際に必要な最小枚数は 2、つまり 49 + 49")
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
/**
|
||||
* File: fractional_knapsack.swift
|
||||
* Created Time: 2023-09-03
|
||||
* Author: nuomi1 (nuomi1@qq.com)
|
||||
*/
|
||||
|
||||
/* 品物 */
|
||||
class Item {
|
||||
var w: Int // 品物の重さ
|
||||
var v: Int // 品物の価値
|
||||
|
||||
init(w: Int, v: Int) {
|
||||
self.w = w
|
||||
self.v = v
|
||||
}
|
||||
}
|
||||
|
||||
/* 分数ナップサック:貪欲法 */
|
||||
func fractionalKnapsack(wgt: [Int], val: [Int], cap: Int) -> Double {
|
||||
// 重さと価値の 2 属性を持つ品物リストを作成
|
||||
var items = zip(wgt, val).map { Item(w: $0, v: $1) }
|
||||
// 単位価値 item.v / item.w の高い順にソートする
|
||||
items.sort { -(Double($0.v) / Double($0.w)) < -(Double($1.v) / Double($1.w)) }
|
||||
// 貪欲選択を繰り返す
|
||||
var res = 0.0
|
||||
var cap = cap
|
||||
for item in items {
|
||||
if item.w <= cap {
|
||||
// 残り容量が十分なら、現在の品物を丸ごとナップサックに入れる
|
||||
res += Double(item.v)
|
||||
cap -= item.w
|
||||
} else {
|
||||
// 残り容量が足りない場合は、現在の品物の一部だけをナップサックに入れる
|
||||
res += Double(item.v) / Double(item.w) * Double(cap)
|
||||
// 残り容量がないため、ループを抜ける
|
||||
break
|
||||
}
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
@main
|
||||
enum FractionalKnapsack {
|
||||
/* Driver Code */
|
||||
static func main() {
|
||||
// 品物の重さ
|
||||
let wgt = [10, 20, 30, 40, 50]
|
||||
// 品物の価値
|
||||
let val = [50, 120, 150, 210, 240]
|
||||
// ナップサック容量
|
||||
let cap = 50
|
||||
|
||||
// 貪欲法
|
||||
let res = fractionalKnapsack(wgt: wgt, val: val, cap: cap)
|
||||
print("ナップサック容量を超えない最大価値は \(res)")
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
/**
|
||||
* File: max_capacity.swift
|
||||
* Created Time: 2023-09-03
|
||||
* Author: nuomi1 (nuomi1@qq.com)
|
||||
*/
|
||||
|
||||
/* 最大容量:貪欲法 */
|
||||
func maxCapacity(ht: [Int]) -> Int {
|
||||
// i, j を初期化し、それぞれ配列の両端に置く
|
||||
var i = ht.startIndex, j = ht.endIndex - 1
|
||||
// 初期の最大容量は 0
|
||||
var res = 0
|
||||
// 2 枚の板が出会うまで貪欲選択を繰り返す
|
||||
while i < j {
|
||||
// 最大容量を更新する
|
||||
let cap = min(ht[i], ht[j]) * (j - i)
|
||||
res = max(res, cap)
|
||||
// 短い方を内側へ動かす
|
||||
if ht[i] < ht[j] {
|
||||
i += 1
|
||||
} else {
|
||||
j -= 1
|
||||
}
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
@main
|
||||
enum MaxCapacity {
|
||||
/* Driver Code */
|
||||
static func main() {
|
||||
let ht = [3, 8, 5, 2, 7, 7, 3, 4]
|
||||
|
||||
// 貪欲法
|
||||
let res = maxCapacity(ht: ht)
|
||||
print("最大容量は \(res)")
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
/**
|
||||
* File: max_product_cutting.swift
|
||||
* Created Time: 2023-09-03
|
||||
* Author: nuomi1 (nuomi1@qq.com)
|
||||
*/
|
||||
|
||||
import Foundation
|
||||
|
||||
func pow(_ x: Int, _ y: Int) -> Int {
|
||||
Int(Double(truncating: pow(Decimal(x), y) as NSDecimalNumber))
|
||||
}
|
||||
|
||||
/* 最大切断積:貪欲法 */
|
||||
func maxProductCutting(n: Int) -> Int {
|
||||
// n <= 3 のときは、必ず 1 を切り出す
|
||||
if n <= 3 {
|
||||
return 1 * (n - 1)
|
||||
}
|
||||
// 貪欲に 3 を切り出し、a を 3 の個数、b を余りとする
|
||||
let a = n / 3
|
||||
let b = n % 3
|
||||
if b == 1 {
|
||||
// 余りが 1 のときは、1 * 3 を 2 * 2 に変える
|
||||
return pow(3, a - 1) * 2 * 2
|
||||
}
|
||||
if b == 2 {
|
||||
// 余りが 2 のときは、そのままにする
|
||||
return pow(3, a) * 2
|
||||
}
|
||||
// 余りが 0 のときは、そのままにする
|
||||
return pow(3, a)
|
||||
}
|
||||
|
||||
@main
|
||||
enum MaxProductCutting {
|
||||
static func main() {
|
||||
let n = 58
|
||||
|
||||
// 貪欲法
|
||||
let res = maxProductCutting(n: n)
|
||||
print("最大分割積は \(res)")
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,110 @@
|
||||
/**
|
||||
* File: array_hash_map.swift
|
||||
* Created Time: 2023-01-16
|
||||
* Author: nuomi1 (nuomi1@qq.com)
|
||||
*/
|
||||
|
||||
import utils
|
||||
|
||||
/* 配列ベースのハッシュテーブル */
|
||||
class ArrayHashMap {
|
||||
private var buckets: [Pair?]
|
||||
|
||||
init() {
|
||||
// 100 個のバケットを含む配列を初期化
|
||||
buckets = Array(repeating: nil, count: 100)
|
||||
}
|
||||
|
||||
/* ハッシュ関数 */
|
||||
private func hashFunc(key: Int) -> Int {
|
||||
let index = key % 100
|
||||
return index
|
||||
}
|
||||
|
||||
/* 検索操作 */
|
||||
func get(key: Int) -> String? {
|
||||
let index = hashFunc(key: key)
|
||||
let pair = buckets[index]
|
||||
return pair?.val
|
||||
}
|
||||
|
||||
/* 追加操作 */
|
||||
func put(key: Int, val: String) {
|
||||
let pair = Pair(key: key, val: val)
|
||||
let index = hashFunc(key: key)
|
||||
buckets[index] = pair
|
||||
}
|
||||
|
||||
/* 削除操作 */
|
||||
func remove(key: Int) {
|
||||
let index = hashFunc(key: key)
|
||||
// nil に設定し、削除を表す
|
||||
buckets[index] = nil
|
||||
}
|
||||
|
||||
/* すべてのキーと値のペアを取得 */
|
||||
func pairSet() -> [Pair] {
|
||||
buckets.compactMap { $0 }
|
||||
}
|
||||
|
||||
/* すべてのキーを取得 */
|
||||
func keySet() -> [Int] {
|
||||
buckets.compactMap { $0?.key }
|
||||
}
|
||||
|
||||
/* すべての値を取得 */
|
||||
func valueSet() -> [String] {
|
||||
buckets.compactMap { $0?.val }
|
||||
}
|
||||
|
||||
/* ハッシュテーブルを出力 */
|
||||
func print() {
|
||||
for pair in pairSet() {
|
||||
Swift.print("\(pair.key) -> \(pair.val)")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@main
|
||||
enum _ArrayHashMap {
|
||||
/* Driver Code */
|
||||
static func main() {
|
||||
/* ハッシュテーブルを初期化 */
|
||||
let map = ArrayHashMap()
|
||||
|
||||
/* 追加操作 */
|
||||
// ハッシュテーブルにキーと値のペア (key, value) を追加
|
||||
map.put(key: 12836, val: "シャオハー")
|
||||
map.put(key: 15937, val: "シャオルオ")
|
||||
map.put(key: 16750, val: "シャオスワン")
|
||||
map.put(key: 13276, val: "シャオファー")
|
||||
map.put(key: 10583, val: "シャオヤー")
|
||||
print("\n追加完了後のハッシュテーブルは\nKey -> Value")
|
||||
map.print()
|
||||
|
||||
/* 検索操作 */
|
||||
// キー key をハッシュテーブルに渡し、値 value を取得
|
||||
let name = map.get(key: 15937)!
|
||||
print("\n学籍番号 15937 を入力すると、名前 \(name) が見つかりました")
|
||||
|
||||
/* 削除操作 */
|
||||
// ハッシュテーブルからキーと値のペア (key, value) を削除
|
||||
map.remove(key: 10583)
|
||||
print("\n10583 を削除後のハッシュテーブルは\nKey -> Value")
|
||||
map.print()
|
||||
|
||||
/* ハッシュテーブルを走査 */
|
||||
print("\nキーと値の組 Key->Value を走査")
|
||||
for pair in map.pairSet() {
|
||||
print("\(pair.key) -> \(pair.val)")
|
||||
}
|
||||
print("\nキー Key を個別に走査")
|
||||
for key in map.keySet() {
|
||||
print(key)
|
||||
}
|
||||
print("\n値 Value を個別に走査")
|
||||
for val in map.valueSet() {
|
||||
print(val)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
/**
|
||||
* File: built_in_hash.swift
|
||||
* Created Time: 2023-07-01
|
||||
* Author: nuomi1 (nuomi1@qq.com)
|
||||
*/
|
||||
|
||||
import utils
|
||||
|
||||
@main
|
||||
enum BuiltInHash {
|
||||
/* Driver Code */
|
||||
static func main() {
|
||||
let num = 3
|
||||
let hashNum = num.hashValue
|
||||
print("整数 \(num) のハッシュ値は \(hashNum)")
|
||||
|
||||
let bol = true
|
||||
let hashBol = bol.hashValue
|
||||
print("真偽値 \(bol) のハッシュ値は \(hashBol)")
|
||||
|
||||
let dec = 3.14159
|
||||
let hashDec = dec.hashValue
|
||||
print("小数 \(dec) のハッシュ値は \(hashDec)")
|
||||
|
||||
let str = "Hello アルゴリズム"
|
||||
let hashStr = str.hashValue
|
||||
print("文字列 \(str) のハッシュ値は \(hashStr)")
|
||||
|
||||
let arr = [AnyHashable(12836), AnyHashable("シャオハー")]
|
||||
let hashTup = arr.hashValue
|
||||
print("配列 \(arr) のハッシュ値は \(hashTup)")
|
||||
|
||||
let obj = ListNode(x: 0)
|
||||
let hashObj = obj.hashValue
|
||||
print("ノードオブジェクト \(obj) のハッシュ値は \(hashObj)")
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
/**
|
||||
* File: hash_map.swift
|
||||
* Created Time: 2023-01-16
|
||||
* Author: nuomi1 (nuomi1@qq.com)
|
||||
*/
|
||||
|
||||
import utils
|
||||
|
||||
@main
|
||||
enum HashMap {
|
||||
/* Driver Code */
|
||||
static func main() {
|
||||
/* ハッシュテーブルを初期化 */
|
||||
var map: [Int: String] = [:]
|
||||
|
||||
/* 追加操作 */
|
||||
// ハッシュテーブルにキーと値のペア (key, value) を追加
|
||||
map[12836] = "シャオハー"
|
||||
map[15937] = "シャオルオ"
|
||||
map[16750] = "シャオスワン"
|
||||
map[13276] = "シャオファー"
|
||||
map[10583] = "シャオヤー"
|
||||
print("\n追加完了後のハッシュテーブルは\nKey -> Value")
|
||||
PrintUtil.printHashMap(map: map)
|
||||
|
||||
/* 検索操作 */
|
||||
// キー key をハッシュテーブルに渡し、値 value を取得
|
||||
let name = map[15937]!
|
||||
print("\n学籍番号 15937 を入力すると、名前 \(name) が見つかりました")
|
||||
|
||||
/* 削除操作 */
|
||||
// ハッシュテーブルからキーと値のペア (key, value) を削除
|
||||
map.removeValue(forKey: 10583)
|
||||
print("\n10583 を削除後のハッシュテーブルは\nKey -> Value")
|
||||
PrintUtil.printHashMap(map: map)
|
||||
|
||||
/* ハッシュテーブルを走査 */
|
||||
print("\nキーと値の組 Key->Value を走査")
|
||||
for (key, value) in map {
|
||||
print("\(key) -> \(value)")
|
||||
}
|
||||
print("\nキー Key を個別に走査")
|
||||
for key in map.keys {
|
||||
print(key)
|
||||
}
|
||||
print("\n値 Value を個別に走査")
|
||||
for value in map.values {
|
||||
print(value)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,138 @@
|
||||
/**
|
||||
* File: hash_map_chaining.swift
|
||||
* Created Time: 2023-06-28
|
||||
* Author: nuomi1 (nuomi1@qq.com)
|
||||
*/
|
||||
|
||||
import utils
|
||||
|
||||
/* チェイン法ハッシュテーブル */
|
||||
class HashMapChaining {
|
||||
var size: Int // キーと値のペア数
|
||||
var capacity: Int // ハッシュテーブル容量
|
||||
var loadThres: Double // リサイズを発動する負荷率のしきい値
|
||||
var extendRatio: Int // 拡張倍率
|
||||
var buckets: [[Pair]] // バケット配列
|
||||
|
||||
/* コンストラクタ */
|
||||
init() {
|
||||
size = 0
|
||||
capacity = 4
|
||||
loadThres = 2.0 / 3.0
|
||||
extendRatio = 2
|
||||
buckets = Array(repeating: [], count: capacity)
|
||||
}
|
||||
|
||||
/* ハッシュ関数 */
|
||||
func hashFunc(key: Int) -> Int {
|
||||
key % capacity
|
||||
}
|
||||
|
||||
/* 負荷率 */
|
||||
func loadFactor() -> Double {
|
||||
Double(size) / Double(capacity)
|
||||
}
|
||||
|
||||
/* 検索操作 */
|
||||
func get(key: Int) -> String? {
|
||||
let index = hashFunc(key: key)
|
||||
let bucket = buckets[index]
|
||||
// バケットを走査し、key が見つかれば対応する val を返す
|
||||
for pair in bucket {
|
||||
if pair.key == key {
|
||||
return pair.val
|
||||
}
|
||||
}
|
||||
// `key` が見つからなければ `nil` を返す
|
||||
return nil
|
||||
}
|
||||
|
||||
/* 追加操作 */
|
||||
func put(key: Int, val: String) {
|
||||
// 負荷率がしきい値を超えたら、リサイズを実行
|
||||
if loadFactor() > loadThres {
|
||||
extend()
|
||||
}
|
||||
let index = hashFunc(key: key)
|
||||
let bucket = buckets[index]
|
||||
// バケットを走査し、指定した key が見つかれば対応する val を更新して返す
|
||||
for pair in bucket {
|
||||
if pair.key == key {
|
||||
pair.val = val
|
||||
return
|
||||
}
|
||||
}
|
||||
// その key が存在しなければ、キーと値のペアを末尾に追加
|
||||
let pair = Pair(key: key, val: val)
|
||||
buckets[index].append(pair)
|
||||
size += 1
|
||||
}
|
||||
|
||||
/* 削除操作 */
|
||||
func remove(key: Int) {
|
||||
let index = hashFunc(key: key)
|
||||
let bucket = buckets[index]
|
||||
// バケットを走査してキーと値のペアを削除
|
||||
for (pairIndex, pair) in bucket.enumerated() {
|
||||
if pair.key == key {
|
||||
buckets[index].remove(at: pairIndex)
|
||||
size -= 1
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* ハッシュテーブルを拡張 */
|
||||
func extend() {
|
||||
// 元のハッシュテーブルを一時保存
|
||||
let bucketsTmp = buckets
|
||||
// リサイズ後の新しいハッシュテーブルを初期化
|
||||
capacity *= extendRatio
|
||||
buckets = Array(repeating: [], count: capacity)
|
||||
size = 0
|
||||
// キーと値のペアを元のハッシュテーブルから新しいハッシュテーブルへ移す
|
||||
for bucket in bucketsTmp {
|
||||
for pair in bucket {
|
||||
put(key: pair.key, val: pair.val)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* ハッシュテーブルを出力 */
|
||||
func print() {
|
||||
for bucket in buckets {
|
||||
let res = bucket.map { "\($0.key) -> \($0.val)" }
|
||||
Swift.print(res)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@main
|
||||
enum _HashMapChaining {
|
||||
/* Driver Code */
|
||||
static func main() {
|
||||
/* ハッシュテーブルを初期化 */
|
||||
let map = HashMapChaining()
|
||||
|
||||
/* 追加操作 */
|
||||
// ハッシュテーブルにキーと値のペア (key, value) を追加
|
||||
map.put(key: 12836, val: "シャオハー")
|
||||
map.put(key: 15937, val: "シャオルオ")
|
||||
map.put(key: 16750, val: "シャオスワン")
|
||||
map.put(key: 13276, val: "シャオファー")
|
||||
map.put(key: 10583, val: "シャオヤー")
|
||||
print("\n追加完了後のハッシュテーブルは\nKey -> Value")
|
||||
map.print()
|
||||
|
||||
/* 検索操作 */
|
||||
// キー key をハッシュテーブルに渡し、値 value を取得
|
||||
let name = map.get(key: 13276)
|
||||
print("\n学籍番号 13276 を入力すると、名前 \(name!) が見つかりました")
|
||||
|
||||
/* 削除操作 */
|
||||
// ハッシュテーブルからキーと値のペア (key, value) を削除
|
||||
map.remove(key: 12836)
|
||||
print("\n12836 を削除後、ハッシュテーブルは\nKey -> Value")
|
||||
map.print()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,164 @@
|
||||
/**
|
||||
* File: hash_map_open_addressing.swift
|
||||
* Created Time: 2023-06-28
|
||||
* Author: nuomi1 (nuomi1@qq.com)
|
||||
*/
|
||||
|
||||
import utils
|
||||
|
||||
/* オープンアドレス法ハッシュテーブル */
|
||||
class HashMapOpenAddressing {
|
||||
var size: Int // キーと値のペア数
|
||||
var capacity: Int // ハッシュテーブル容量
|
||||
var loadThres: Double // リサイズを発動する負荷率のしきい値
|
||||
var extendRatio: Int // 拡張倍率
|
||||
var buckets: [Pair?] // バケット配列
|
||||
var TOMBSTONE: Pair // 削除済みマーク
|
||||
|
||||
/* コンストラクタ */
|
||||
init() {
|
||||
size = 0
|
||||
capacity = 4
|
||||
loadThres = 2.0 / 3.0
|
||||
extendRatio = 2
|
||||
buckets = Array(repeating: nil, count: capacity)
|
||||
TOMBSTONE = Pair(key: -1, val: "-1")
|
||||
}
|
||||
|
||||
/* ハッシュ関数 */
|
||||
func hashFunc(key: Int) -> Int {
|
||||
key % capacity
|
||||
}
|
||||
|
||||
/* 負荷率 */
|
||||
func loadFactor() -> Double {
|
||||
Double(size) / Double(capacity)
|
||||
}
|
||||
|
||||
/* key に対応するバケットインデックスを探す */
|
||||
func findBucket(key: Int) -> Int {
|
||||
var index = hashFunc(key: key)
|
||||
var firstTombstone = -1
|
||||
// 線形プロービングを行い、空バケットに達したら終了
|
||||
while buckets[index] != nil {
|
||||
// key が見つかったら、対応するバケットのインデックスを返す
|
||||
if buckets[index]!.key == key {
|
||||
// 以前に削除マークが見つかっていれば、そのインデックスへキーと値のペアを移動
|
||||
if firstTombstone != -1 {
|
||||
buckets[firstTombstone] = buckets[index]
|
||||
buckets[index] = TOMBSTONE
|
||||
return firstTombstone // 移動後のバケットインデックスを返す
|
||||
}
|
||||
return index // バケットのインデックスを返す
|
||||
}
|
||||
// 最初に見つかった削除マークを記録
|
||||
if firstTombstone == -1 && buckets[index] == TOMBSTONE {
|
||||
firstTombstone = index
|
||||
}
|
||||
// バケットのインデックスを計算し、末尾を越えたら先頭に戻る
|
||||
index = (index + 1) % capacity
|
||||
}
|
||||
// key が存在しない場合は追加位置のインデックスを返す
|
||||
return firstTombstone == -1 ? index : firstTombstone
|
||||
}
|
||||
|
||||
/* 検索操作 */
|
||||
func get(key: Int) -> String? {
|
||||
// key に対応するバケットインデックスを探す
|
||||
let index = findBucket(key: key)
|
||||
// キーと値の組が見つかったら、対応する val を返す
|
||||
if buckets[index] != nil, buckets[index] != TOMBSTONE {
|
||||
return buckets[index]!.val
|
||||
}
|
||||
// キーと値の組が存在しなければ null を返す
|
||||
return nil
|
||||
}
|
||||
|
||||
/* 追加操作 */
|
||||
func put(key: Int, val: String) {
|
||||
// 負荷率がしきい値を超えたら、リサイズを実行
|
||||
if loadFactor() > loadThres {
|
||||
extend()
|
||||
}
|
||||
// key に対応するバケットインデックスを探す
|
||||
let index = findBucket(key: key)
|
||||
// キーと値の組が見つかったら、val を上書きして返す
|
||||
if buckets[index] != nil, buckets[index] != TOMBSTONE {
|
||||
buckets[index]!.val = val
|
||||
return
|
||||
}
|
||||
// キーと値の組が存在しない場合は、その組を追加する
|
||||
buckets[index] = Pair(key: key, val: val)
|
||||
size += 1
|
||||
}
|
||||
|
||||
/* 削除操作 */
|
||||
func remove(key: Int) {
|
||||
// key に対応するバケットインデックスを探す
|
||||
let index = findBucket(key: key)
|
||||
// キーと値の組が見つかったら、削除マーカーで上書きする
|
||||
if buckets[index] != nil, buckets[index] != TOMBSTONE {
|
||||
buckets[index] = TOMBSTONE
|
||||
size -= 1
|
||||
}
|
||||
}
|
||||
|
||||
/* ハッシュテーブルを拡張 */
|
||||
func extend() {
|
||||
// 元のハッシュテーブルを一時保存
|
||||
let bucketsTmp = buckets
|
||||
// リサイズ後の新しいハッシュテーブルを初期化
|
||||
capacity *= extendRatio
|
||||
buckets = Array(repeating: nil, count: capacity)
|
||||
size = 0
|
||||
// キーと値のペアを元のハッシュテーブルから新しいハッシュテーブルへ移す
|
||||
for pair in bucketsTmp {
|
||||
if let pair, pair != TOMBSTONE {
|
||||
put(key: pair.key, val: pair.val)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* ハッシュテーブルを出力 */
|
||||
func print() {
|
||||
for pair in buckets {
|
||||
if pair == nil {
|
||||
Swift.print("null")
|
||||
} else if pair == TOMBSTONE {
|
||||
Swift.print("TOMBSTONE")
|
||||
} else {
|
||||
Swift.print("\(pair!.key) -> \(pair!.val)")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@main
|
||||
enum _HashMapOpenAddressing {
|
||||
/* Driver Code */
|
||||
static func main() {
|
||||
/* ハッシュテーブルを初期化 */
|
||||
let map = HashMapOpenAddressing()
|
||||
|
||||
/* 追加操作 */
|
||||
// ハッシュテーブルにキーと値のペア (key, value) を追加
|
||||
map.put(key: 12836, val: "シャオハー")
|
||||
map.put(key: 15937, val: "シャオルオ")
|
||||
map.put(key: 16750, val: "シャオスワン")
|
||||
map.put(key: 13276, val: "シャオファー")
|
||||
map.put(key: 10583, val: "シャオヤー")
|
||||
print("\n追加完了後のハッシュテーブルは\nKey -> Value")
|
||||
map.print()
|
||||
|
||||
/* 検索操作 */
|
||||
// キー key をハッシュテーブルに渡し、値 value を取得
|
||||
let name = map.get(key: 13276)
|
||||
print("\n学籍番号 13276 を入力すると、名前 \(name!) が見つかりました")
|
||||
|
||||
/* 削除操作 */
|
||||
// ハッシュテーブルからキーと値のペア (key, value) を削除
|
||||
map.remove(key: 16750)
|
||||
print("\n16750 を削除後、ハッシュテーブルは\nKey -> Value")
|
||||
map.print()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
/**
|
||||
* File: simple_hash.swift
|
||||
* Created Time: 2023-07-01
|
||||
* Author: nuomi1 (nuomi1@qq.com)
|
||||
*/
|
||||
|
||||
/* 加算ハッシュ */
|
||||
func addHash(key: String) -> Int {
|
||||
var hash = 0
|
||||
let MODULUS = 1_000_000_007
|
||||
for c in key {
|
||||
for scalar in c.unicodeScalars {
|
||||
hash = (hash + Int(scalar.value)) % MODULUS
|
||||
}
|
||||
}
|
||||
return hash
|
||||
}
|
||||
|
||||
/* 乗算ハッシュ */
|
||||
func mulHash(key: String) -> Int {
|
||||
var hash = 0
|
||||
let MODULUS = 1_000_000_007
|
||||
for c in key {
|
||||
for scalar in c.unicodeScalars {
|
||||
hash = (31 * hash + Int(scalar.value)) % MODULUS
|
||||
}
|
||||
}
|
||||
return hash
|
||||
}
|
||||
|
||||
/* XOR ハッシュ */
|
||||
func xorHash(key: String) -> Int {
|
||||
var hash = 0
|
||||
let MODULUS = 1_000_000_007
|
||||
for c in key {
|
||||
for scalar in c.unicodeScalars {
|
||||
hash ^= Int(scalar.value)
|
||||
}
|
||||
}
|
||||
return hash & MODULUS
|
||||
}
|
||||
|
||||
/* 回転ハッシュ */
|
||||
func rotHash(key: String) -> Int {
|
||||
var hash = 0
|
||||
let MODULUS = 1_000_000_007
|
||||
for c in key {
|
||||
for scalar in c.unicodeScalars {
|
||||
hash = ((hash << 4) ^ (hash >> 28) ^ Int(scalar.value)) % MODULUS
|
||||
}
|
||||
}
|
||||
return hash
|
||||
}
|
||||
|
||||
@main
|
||||
enum SimpleHash {
|
||||
/* Driver Code */
|
||||
static func main() {
|
||||
let key = "Hello アルゴリズム"
|
||||
|
||||
var hash = addHash(key: key)
|
||||
print("加算ハッシュ値は \(hash)")
|
||||
|
||||
hash = mulHash(key: key)
|
||||
print("乗算ハッシュ値は \(hash)")
|
||||
|
||||
hash = xorHash(key: key)
|
||||
print("XORハッシュ値は \(hash)")
|
||||
|
||||
hash = rotHash(key: key)
|
||||
print("回転ハッシュ値は \(hash)")
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
/**
|
||||
* File: heap.swift
|
||||
* Created Time: 2024-03-17
|
||||
* Author: nuomi1 (nuomi1@qq.com)
|
||||
*/
|
||||
|
||||
import HeapModule
|
||||
import utils
|
||||
|
||||
func testPush(heap: inout Heap<Int>, val: Int) {
|
||||
heap.insert(val)
|
||||
print("\n要素 \(val) をヒープに追加した後\n")
|
||||
PrintUtil.printHeap(queue: heap.unordered)
|
||||
}
|
||||
|
||||
func testPop(heap: inout Heap<Int>) {
|
||||
let val = heap.removeMax()
|
||||
print("\nヒープ先頭要素 \(val) を取り出した後\n")
|
||||
PrintUtil.printHeap(queue: heap.unordered)
|
||||
}
|
||||
|
||||
@main
|
||||
enum _Heap {
|
||||
/* Driver Code */
|
||||
static func main() {
|
||||
/* ヒープを初期化 */
|
||||
// Swift の Heap 型は最大ヒープと最小ヒープの両方をサポートする
|
||||
var heap = Heap<Int>()
|
||||
|
||||
/* 要素をヒープに追加 */
|
||||
testPush(heap: &heap, val: 1)
|
||||
testPush(heap: &heap, val: 3)
|
||||
testPush(heap: &heap, val: 2)
|
||||
testPush(heap: &heap, val: 5)
|
||||
testPush(heap: &heap, val: 4)
|
||||
|
||||
/* ヒープ頂点の要素を取得 */
|
||||
let peek = heap.max()
|
||||
print("\nヒープ先頭要素は \(peek!)\n")
|
||||
|
||||
/* ヒープ頂点の要素を取り出す */
|
||||
testPop(heap: &heap)
|
||||
testPop(heap: &heap)
|
||||
testPop(heap: &heap)
|
||||
testPop(heap: &heap)
|
||||
testPop(heap: &heap)
|
||||
|
||||
/* ヒープのサイズを取得 */
|
||||
let size = heap.count
|
||||
print("\nヒープ内の要素数は \(size)\n")
|
||||
|
||||
/* ヒープが空かどうかを判定 */
|
||||
let isEmpty = heap.isEmpty
|
||||
print("\nヒープが空かどうか \(isEmpty)\n")
|
||||
|
||||
/* リストを入力してヒープを構築 */
|
||||
// 時間計算量は O(n) であり、O(nlogn) ではない
|
||||
let heap2 = Heap([1, 3, 2, 5, 4])
|
||||
print("\nリストを入力してヒープを構築した後")
|
||||
PrintUtil.printHeap(queue: heap2.unordered)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,163 @@
|
||||
/**
|
||||
* File: my_heap.swift
|
||||
* Created Time: 2023-01-28
|
||||
* Author: nuomi1 (nuomi1@qq.com)
|
||||
*/
|
||||
|
||||
import utils
|
||||
|
||||
/* 最大ヒープ */
|
||||
class MaxHeap {
|
||||
private var maxHeap: [Int]
|
||||
|
||||
/* コンストラクタ。入力リストに基づいてヒープを構築する */
|
||||
init(nums: [Int]) {
|
||||
// リスト要素をそのままヒープに追加
|
||||
maxHeap = nums
|
||||
// 葉ノード以外のすべてのノードをヒープ化
|
||||
for i in (0 ... parent(i: size() - 1)).reversed() {
|
||||
siftDown(i: i)
|
||||
}
|
||||
}
|
||||
|
||||
/* 左子ノードのインデックスを取得 */
|
||||
private func left(i: Int) -> Int {
|
||||
2 * i + 1
|
||||
}
|
||||
|
||||
/* 右子ノードのインデックスを取得 */
|
||||
private func right(i: Int) -> Int {
|
||||
2 * i + 2
|
||||
}
|
||||
|
||||
/* 親ノードのインデックスを取得 */
|
||||
private func parent(i: Int) -> Int {
|
||||
(i - 1) / 2 // 切り捨て除算
|
||||
}
|
||||
|
||||
/* 要素を交換 */
|
||||
private func swap(i: Int, j: Int) {
|
||||
maxHeap.swapAt(i, j)
|
||||
}
|
||||
|
||||
/* ヒープのサイズを取得 */
|
||||
func size() -> Int {
|
||||
maxHeap.count
|
||||
}
|
||||
|
||||
/* ヒープが空かどうかを判定 */
|
||||
func isEmpty() -> Bool {
|
||||
size() == 0
|
||||
}
|
||||
|
||||
/* ヒープ先頭要素にアクセス */
|
||||
func peek() -> Int {
|
||||
maxHeap[0]
|
||||
}
|
||||
|
||||
/* 要素をヒープに追加 */
|
||||
func push(val: Int) {
|
||||
// ノードを追加
|
||||
maxHeap.append(val)
|
||||
// 下から上へヒープ化
|
||||
siftUp(i: size() - 1)
|
||||
}
|
||||
|
||||
/* ノード i から始めて、下から上へヒープ化 */
|
||||
private func siftUp(i: Int) {
|
||||
var i = i
|
||||
while true {
|
||||
// ノード i の親ノードを取得
|
||||
let p = parent(i: i)
|
||||
// 「根ノードを越えた」または「ノードの修復が不要」になったらヒープ化を終了
|
||||
if p < 0 || maxHeap[i] <= maxHeap[p] {
|
||||
break
|
||||
}
|
||||
// 2 つのノードを交換
|
||||
swap(i: i, j: p)
|
||||
// ループで下から上へヒープ化
|
||||
i = p
|
||||
}
|
||||
}
|
||||
|
||||
/* 要素をヒープから取り出す */
|
||||
func pop() -> Int {
|
||||
// 空判定の処理
|
||||
if isEmpty() {
|
||||
fatalError("ヒープが空です")
|
||||
}
|
||||
// 根ノードと最も右の葉ノードを交換(先頭要素と末尾要素を交換)
|
||||
swap(i: 0, j: size() - 1)
|
||||
// ノードを削除
|
||||
let val = maxHeap.remove(at: size() - 1)
|
||||
// 上から下へヒープ化
|
||||
siftDown(i: 0)
|
||||
// ヒープ先頭要素を返す
|
||||
return val
|
||||
}
|
||||
|
||||
/* ノード i から始めて、上から下へヒープ化 */
|
||||
private func siftDown(i: Int) {
|
||||
var i = i
|
||||
while true {
|
||||
// ノード i, l, r のうち値が最大のノードを ma とする
|
||||
let l = left(i: i)
|
||||
let r = right(i: i)
|
||||
var ma = i
|
||||
if l < size(), maxHeap[l] > maxHeap[ma] {
|
||||
ma = l
|
||||
}
|
||||
if r < size(), maxHeap[r] > maxHeap[ma] {
|
||||
ma = r
|
||||
}
|
||||
// ノード i が最大、またはインデックス l, r が範囲外なら、ヒープ化は不要なので抜ける
|
||||
if ma == i {
|
||||
break
|
||||
}
|
||||
// 2 つのノードを交換
|
||||
swap(i: i, j: ma)
|
||||
// ループで上から下へヒープ化
|
||||
i = ma
|
||||
}
|
||||
}
|
||||
|
||||
/* ヒープ(二分木)を出力 */
|
||||
func print() {
|
||||
let queue = maxHeap
|
||||
PrintUtil.printHeap(queue: queue)
|
||||
}
|
||||
}
|
||||
|
||||
@main
|
||||
enum MyHeap {
|
||||
/* Driver Code */
|
||||
static func main() {
|
||||
/* 最大ヒープを初期化 */
|
||||
let maxHeap = MaxHeap(nums: [9, 8, 6, 6, 7, 5, 2, 1, 4, 3, 6, 2])
|
||||
print("\nリストを入力してヒープを構築した後")
|
||||
maxHeap.print()
|
||||
|
||||
/* ヒープ頂点の要素を取得 */
|
||||
var peek = maxHeap.peek()
|
||||
print("\nヒープ先頭要素は \(peek)")
|
||||
|
||||
/* 要素をヒープに追加 */
|
||||
let val = 7
|
||||
maxHeap.push(val: val)
|
||||
print("\n要素 \(val) をヒープに追加した後")
|
||||
maxHeap.print()
|
||||
|
||||
/* ヒープ頂点の要素を取り出す */
|
||||
peek = maxHeap.pop()
|
||||
print("\nヒープ先頭要素 \(peek) を取り出した後")
|
||||
maxHeap.print()
|
||||
|
||||
/* ヒープのサイズを取得 */
|
||||
let size = maxHeap.size()
|
||||
print("\nヒープ内の要素数は \(size)")
|
||||
|
||||
/* ヒープが空かどうかを判定 */
|
||||
let isEmpty = maxHeap.isEmpty()
|
||||
print("\nヒープが空かどうか \(isEmpty)")
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
/**
|
||||
* File: top_k.swift
|
||||
* Created Time: 2023-07-02
|
||||
* Author: nuomi1 (nuomi1@qq.com)
|
||||
*/
|
||||
|
||||
import HeapModule
|
||||
import utils
|
||||
|
||||
/* ヒープに基づいて配列中の最大の k 個の要素を探す */
|
||||
func topKHeap(nums: [Int], k: Int) -> [Int] {
|
||||
// 最小ヒープを初期化し、先頭 k 個の要素でヒープを構築する
|
||||
var heap = Heap(nums.prefix(k))
|
||||
// k+1 番目の要素から開始し、ヒープ長を k に保つ
|
||||
for i in nums.indices.dropFirst(k) {
|
||||
// 現在の要素がヒープ先頭より大きければ、ヒープ先頭を取り出して現在の要素を追加する
|
||||
if nums[i] > heap.min()! {
|
||||
_ = heap.removeMin()
|
||||
heap.insert(nums[i])
|
||||
}
|
||||
}
|
||||
return heap.unordered
|
||||
}
|
||||
|
||||
@main
|
||||
enum TopK {
|
||||
/* Driver Code */
|
||||
static func main() {
|
||||
let nums = [1, 7, 6, 3, 2]
|
||||
let k = 3
|
||||
|
||||
let res = topKHeap(nums: nums, k: k)
|
||||
print("最大の \(k) 個の要素は")
|
||||
PrintUtil.printHeap(queue: res)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
/**
|
||||
* File: binary_search.swift
|
||||
* Created Time: 2023-01-28
|
||||
* Author: nuomi1 (nuomi1@qq.com)
|
||||
*/
|
||||
|
||||
/* 二分探索(両閉区間) */
|
||||
func binarySearch(nums: [Int], target: Int) -> Int {
|
||||
// 両閉区間 [0, n-1] を初期化する。つまり i, j はそれぞれ配列の先頭要素と末尾要素を指す
|
||||
var i = nums.startIndex
|
||||
var j = nums.endIndex - 1
|
||||
// ループし、探索区間が空になったら終了する(i > j で空)
|
||||
while i <= j {
|
||||
let m = i + (j - i) / 2 // 中点インデックス m を計算
|
||||
if nums[m] < target { // この場合、target は区間 [m+1, j] にある
|
||||
i = m + 1
|
||||
} else if nums[m] > target { // この場合、target は区間 [i, m-1] にある
|
||||
j = m - 1
|
||||
} else { // 目標要素が見つかったらそのインデックスを返す
|
||||
return m
|
||||
}
|
||||
}
|
||||
// 目標要素が見つからなければ -1 を返す
|
||||
return -1
|
||||
}
|
||||
|
||||
/* 二分探索(左閉右開区間) */
|
||||
func binarySearchLCRO(nums: [Int], target: Int) -> Int {
|
||||
// 左閉右開区間 [0, n) を初期化する。つまり i, j はそれぞれ配列の先頭要素と末尾要素+1を指す
|
||||
var i = nums.startIndex
|
||||
var j = nums.endIndex
|
||||
// ループし、探索区間が空になったら終了する(i = j で空)
|
||||
while i < j {
|
||||
let m = i + (j - i) / 2 // 中点インデックス m を計算
|
||||
if nums[m] < target { // この場合、target は区間 [m+1, j) にある
|
||||
i = m + 1
|
||||
} else if nums[m] > target { // この場合、target は区間 [i, m) にある
|
||||
j = m
|
||||
} else { // 目標要素が見つかったらそのインデックスを返す
|
||||
return m
|
||||
}
|
||||
}
|
||||
// 目標要素が見つからなければ -1 を返す
|
||||
return -1
|
||||
}
|
||||
|
||||
@main
|
||||
enum BinarySearch {
|
||||
/* Driver Code */
|
||||
static func main() {
|
||||
let target = 6
|
||||
let nums = [1, 3, 6, 8, 12, 15, 23, 26, 31, 35]
|
||||
|
||||
/* 二分探索(両閉区間) */
|
||||
var index = binarySearch(nums: nums, target: target)
|
||||
print("対象要素 6 のインデックス = \(index)")
|
||||
|
||||
/* 二分探索(左閉右開区間) */
|
||||
index = binarySearchLCRO(nums: nums, target: target)
|
||||
print("対象要素 6 のインデックス = \(index)")
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
/**
|
||||
* File: binary_search_edge.swift
|
||||
* Created Time: 2023-08-06
|
||||
* Author: nuomi1 (nuomi1@qq.com)
|
||||
*/
|
||||
|
||||
import binary_search_insertion_target
|
||||
|
||||
/* 最も左の target を二分探索 */
|
||||
func binarySearchLeftEdge(nums: [Int], target: Int) -> Int {
|
||||
// target の挿入位置を探すのと等価
|
||||
let i = binarySearchInsertion(nums: nums, target: target)
|
||||
// target が見つからなければ、-1 を返す
|
||||
if i == nums.endIndex || nums[i] != target {
|
||||
return -1
|
||||
}
|
||||
// target が見つかったら、インデックス i を返す
|
||||
return i
|
||||
}
|
||||
|
||||
/* 最も右の target を二分探索 */
|
||||
func binarySearchRightEdge(nums: [Int], target: Int) -> Int {
|
||||
// 最左の target + 1 を探す問題に変換する
|
||||
let i = binarySearchInsertion(nums: nums, target: target + 1)
|
||||
// j は最も右の target を指し、i は target より大きい最初の要素を指す
|
||||
let j = i - 1
|
||||
// target が見つからなければ、-1 を返す
|
||||
if j == -1 || nums[j] != target {
|
||||
return -1
|
||||
}
|
||||
// target が見つかったら、インデックス j を返す
|
||||
return j
|
||||
}
|
||||
|
||||
@main
|
||||
enum BinarySearchEdge {
|
||||
/* Driver Code */
|
||||
static func main() {
|
||||
// 重複要素を含む配列
|
||||
let nums = [1, 3, 6, 6, 6, 6, 6, 10, 12, 15]
|
||||
print("\n配列 nums = \(nums)")
|
||||
|
||||
// 二分探索で左端と右端を探す
|
||||
for target in [6, 7] {
|
||||
var index = binarySearchLeftEdge(nums: nums, target: target)
|
||||
print("最も左にある要素 \(target) のインデックスは \(index)")
|
||||
index = binarySearchRightEdge(nums: nums, target: target)
|
||||
print("最も右にある要素 \(target) のインデックスは \(index)")
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
/**
|
||||
* File: binary_search_insertion.swift
|
||||
* Created Time: 2023-08-06
|
||||
* Author: nuomi1 (nuomi1@qq.com)
|
||||
*/
|
||||
|
||||
/* 二分探索で挿入位置を探す(重複要素なし) */
|
||||
func binarySearchInsertionSimple(nums: [Int], target: Int) -> Int {
|
||||
// 両閉区間 [0, n-1] を初期化
|
||||
var i = nums.startIndex
|
||||
var j = nums.endIndex - 1
|
||||
while i <= j {
|
||||
let m = i + (j - i) / 2 // 中点インデックス m を計算
|
||||
if nums[m] < target {
|
||||
i = m + 1 // target は区間 [m+1, j] にある
|
||||
} else if nums[m] > target {
|
||||
j = m - 1 // target は区間 [i, m-1] にある
|
||||
} else {
|
||||
return m // target が見つかったら、挿入位置 m を返す
|
||||
}
|
||||
}
|
||||
// target が見つからなければ、挿入位置 i を返す
|
||||
return i
|
||||
}
|
||||
|
||||
/* 二分探索で挿入位置を探す(重複要素あり) */
|
||||
public func binarySearchInsertion(nums: [Int], target: Int) -> Int {
|
||||
// 両閉区間 [0, n-1] を初期化
|
||||
var i = nums.startIndex
|
||||
var j = nums.endIndex - 1
|
||||
while i <= j {
|
||||
let m = i + (j - i) / 2 // 中点インデックス m を計算
|
||||
if nums[m] < target {
|
||||
i = m + 1 // target は区間 [m+1, j] にある
|
||||
} else if nums[m] > target {
|
||||
j = m - 1 // target は区間 [i, m-1] にある
|
||||
} else {
|
||||
j = m - 1 // target より小さい最初の要素は区間 [i, m-1] にある
|
||||
}
|
||||
}
|
||||
// 挿入位置 i を返す
|
||||
return i
|
||||
}
|
||||
|
||||
#if !TARGET
|
||||
|
||||
@main
|
||||
enum BinarySearchInsertion {
|
||||
/* Driver Code */
|
||||
static func main() {
|
||||
// 重複要素のない配列
|
||||
var nums = [1, 3, 6, 8, 12, 15, 23, 26, 31, 35]
|
||||
print("\n配列 nums = \(nums)")
|
||||
// 二分探索で挿入位置を探す
|
||||
for target in [6, 9] {
|
||||
let index = binarySearchInsertionSimple(nums: nums, target: target)
|
||||
print("要素 \(target) の挿入位置のインデックスは \(index)")
|
||||
}
|
||||
|
||||
// 重複要素を含む配列
|
||||
nums = [1, 3, 6, 6, 6, 6, 6, 10, 12, 15]
|
||||
print("\n配列 nums = \(nums)")
|
||||
// 二分探索で挿入位置を探す
|
||||
for target in [2, 6, 20] {
|
||||
let index = binarySearchInsertion(nums: nums, target: target)
|
||||
print("要素 \(target) の挿入位置のインデックスは \(index)")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,71 @@
|
||||
/**
|
||||
* File: binary_search_insertion.swift
|
||||
* Created Time: 2023-08-06
|
||||
* Author: nuomi1 (nuomi1@qq.com)
|
||||
*/
|
||||
|
||||
/* 二分探索で挿入位置を探す(重複要素なし) */
|
||||
func binarySearchInsertionSimple(nums: [Int], target: Int) -> Int {
|
||||
// 両閉区間 [0, n-1] を初期化
|
||||
var i = nums.startIndex
|
||||
var j = nums.endIndex - 1
|
||||
while i <= j {
|
||||
let m = i + (j - i) / 2 // 中点インデックス m を計算
|
||||
if nums[m] < target {
|
||||
i = m + 1 // target は区間 [m+1, j] にある
|
||||
} else if nums[m] > target {
|
||||
j = m - 1 // target は区間 [i, m-1] にある
|
||||
} else {
|
||||
return m // target が見つかったら、挿入位置 m を返す
|
||||
}
|
||||
}
|
||||
// target が見つからなければ、挿入位置 i を返す
|
||||
return i
|
||||
}
|
||||
|
||||
/* 二分探索で挿入位置を探す(重複要素あり) */
|
||||
public func binarySearchInsertion(nums: [Int], target: Int) -> Int {
|
||||
// 両閉区間 [0, n-1] を初期化
|
||||
var i = nums.startIndex
|
||||
var j = nums.endIndex - 1
|
||||
while i <= j {
|
||||
let m = i + (j - i) / 2 // 中点インデックス m を計算
|
||||
if nums[m] < target {
|
||||
i = m + 1 // target は区間 [m+1, j] にある
|
||||
} else if nums[m] > target {
|
||||
j = m - 1 // target は区間 [i, m-1] にある
|
||||
} else {
|
||||
j = m - 1 // target より小さい最初の要素は区間 [i, m-1] にある
|
||||
}
|
||||
}
|
||||
// 挿入位置 i を返す
|
||||
return i
|
||||
}
|
||||
|
||||
#if !TARGET
|
||||
|
||||
@main
|
||||
enum BinarySearchInsertion {
|
||||
/* Driver Code */
|
||||
static func main() {
|
||||
// 重複要素のない配列
|
||||
var nums = [1, 3, 6, 8, 12, 15, 23, 26, 31, 35]
|
||||
print("\n配列 nums = \(nums)")
|
||||
// 二分探索で挿入位置を探す
|
||||
for target in [6, 9] {
|
||||
let index = binarySearchInsertionSimple(nums: nums, target: target)
|
||||
print("要素 \(target) の挿入位置のインデックスは \(index)")
|
||||
}
|
||||
|
||||
// 重複要素を含む配列
|
||||
nums = [1, 3, 6, 6, 6, 6, 6, 10, 12, 15]
|
||||
print("\n配列 nums = \(nums)")
|
||||
// 二分探索で挿入位置を探す
|
||||
for target in [2, 6, 20] {
|
||||
let index = binarySearchInsertion(nums: nums, target: target)
|
||||
print("要素 \(target) の挿入位置のインデックスは \(index)")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,50 @@
|
||||
/**
|
||||
* File: hashing_search.swift
|
||||
* Created Time: 2023-01-28
|
||||
* Author: nuomi1 (nuomi1@qq.com)
|
||||
*/
|
||||
|
||||
import utils
|
||||
|
||||
/* ハッシュ探索(配列) */
|
||||
func hashingSearchArray(map: [Int: Int], target: Int) -> Int {
|
||||
// ハッシュテーブルの key: 目標要素、value: インデックス
|
||||
// ハッシュテーブルにこの key がなければ -1 を返す
|
||||
return map[target, default: -1]
|
||||
}
|
||||
|
||||
/* ハッシュ探索(連結リスト) */
|
||||
func hashingSearchLinkedList(map: [Int: ListNode], target: Int) -> ListNode? {
|
||||
// ハッシュテーブルの key: 目標ノード値、value: ノードオブジェクト
|
||||
// ハッシュテーブルにこの key がなければ null を返す
|
||||
return map[target]
|
||||
}
|
||||
|
||||
@main
|
||||
enum HashingSearch {
|
||||
/* Driver Code */
|
||||
static func main() {
|
||||
let target = 3
|
||||
|
||||
/* ハッシュ探索(配列) */
|
||||
let nums = [1, 5, 3, 2, 4, 7, 5, 9, 10, 8]
|
||||
// ハッシュテーブルを初期化
|
||||
var map: [Int: Int] = [:]
|
||||
for i in nums.indices {
|
||||
map[nums[i]] = i // key: 要素、value: インデックス
|
||||
}
|
||||
let index = hashingSearchArray(map: map, target: target)
|
||||
print("目標要素 3 のインデックス = \(index)")
|
||||
|
||||
/* ハッシュ探索(連結リスト) */
|
||||
var head = ListNode.arrToLinkedList(arr: nums)
|
||||
// ハッシュテーブルを初期化
|
||||
var map1: [Int: ListNode] = [:]
|
||||
while head != nil {
|
||||
map1[head!.val] = head! // key: ノード値、value: ノード
|
||||
head = head?.next
|
||||
}
|
||||
let node = hashingSearchLinkedList(map: map1, target: target)
|
||||
print("目標ノード値 3 に対応するノードオブジェクトは \(node!)")
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
/**
|
||||
* File: linear_search.swift
|
||||
* Created Time: 2023-01-28
|
||||
* Author: nuomi1 (nuomi1@qq.com)
|
||||
*/
|
||||
|
||||
import utils
|
||||
|
||||
/* 線形探索(配列) */
|
||||
func linearSearchArray(nums: [Int], target: Int) -> Int {
|
||||
// 配列を走査
|
||||
for i in nums.indices {
|
||||
// 目標要素が見つかったらそのインデックスを返す
|
||||
if nums[i] == target {
|
||||
return i
|
||||
}
|
||||
}
|
||||
// 目標要素が見つからなければ -1 を返す
|
||||
return -1
|
||||
}
|
||||
|
||||
/* 線形探索(連結リスト) */
|
||||
func linearSearchLinkedList(head: ListNode?, target: Int) -> ListNode? {
|
||||
var head = head
|
||||
// 連結リストを走査
|
||||
while head != nil {
|
||||
// 対象ノードが見つかったら、それを返す
|
||||
if head?.val == target {
|
||||
return head
|
||||
}
|
||||
head = head?.next
|
||||
}
|
||||
// 対象ノードが見つからない場合は null を返す
|
||||
return nil
|
||||
}
|
||||
|
||||
@main
|
||||
enum LinearSearch {
|
||||
/* Driver Code */
|
||||
static func main() {
|
||||
let target = 3
|
||||
|
||||
/* 配列で線形探索を行う */
|
||||
let nums = [1, 5, 3, 2, 4, 7, 5, 9, 10, 8]
|
||||
let index = linearSearchArray(nums: nums, target: target)
|
||||
print("目標要素 3 のインデックス = \(index)")
|
||||
|
||||
/* 連結リストで線形探索を行う */
|
||||
let head = ListNode.arrToLinkedList(arr: nums)
|
||||
let node = linearSearchLinkedList(head: head, target: target)
|
||||
print("目標ノード値 3 に対応するノードオブジェクトは \(node!)")
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
/**
|
||||
* File: two_sum.swift
|
||||
* Created Time: 2023-01-03
|
||||
* Author: nuomi1 (nuomi1@qq.com)
|
||||
*/
|
||||
|
||||
/* 方法 1:総当たり列挙 */
|
||||
func twoSumBruteForce(nums: [Int], target: Int) -> [Int] {
|
||||
// 2重ループのため、時間計算量は O(n^2)
|
||||
for i in nums.indices.dropLast() {
|
||||
for j in nums.indices.dropFirst(i + 1) {
|
||||
if nums[i] + nums[j] == target {
|
||||
return [i, j]
|
||||
}
|
||||
}
|
||||
}
|
||||
return [0]
|
||||
}
|
||||
|
||||
/* 方法 2:補助ハッシュテーブル */
|
||||
func twoSumHashTable(nums: [Int], target: Int) -> [Int] {
|
||||
// 補助ハッシュテーブルを使用し、空間計算量は O(n)
|
||||
var dic: [Int: Int] = [:]
|
||||
// 単一ループで、時間計算量は O(n)
|
||||
for i in nums.indices {
|
||||
if let j = dic[target - nums[i]] {
|
||||
return [j, i]
|
||||
}
|
||||
dic[nums[i]] = i
|
||||
}
|
||||
return [0]
|
||||
}
|
||||
|
||||
@main
|
||||
enum LeetcodeTwoSum {
|
||||
/* Driver Code */
|
||||
static func main() {
|
||||
// ======= Test Case =======
|
||||
let nums = [2, 7, 11, 15]
|
||||
let target = 13
|
||||
// ====== Driver Code ======
|
||||
// 方法 1
|
||||
var res = twoSumBruteForce(nums: nums, target: target)
|
||||
print("方法1 res = \(res)")
|
||||
// 方法 2
|
||||
res = twoSumHashTable(nums: nums, target: target)
|
||||
print("方法2 res = \(res)")
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
/**
|
||||
* File: bubble_sort.swift
|
||||
* Created Time: 2023-01-29
|
||||
* Author: nuomi1 (nuomi1@qq.com)
|
||||
*/
|
||||
|
||||
/* バブルソート */
|
||||
func bubbleSort(nums: inout [Int]) {
|
||||
// 外側のループ:未ソート区間は [0, i]
|
||||
for i in nums.indices.dropFirst().reversed() {
|
||||
// 内側のループ:未ソート区間 [0, i] の最大要素をその区間の最右端へ交換
|
||||
for j in 0 ..< i {
|
||||
if nums[j] > nums[j + 1] {
|
||||
// nums[j] と nums[j + 1] を交換
|
||||
nums.swapAt(j, j + 1)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* バブルソート(フラグ最適化) */
|
||||
func bubbleSortWithFlag(nums: inout [Int]) {
|
||||
// 外側のループ:未ソート区間は [0, i]
|
||||
for i in nums.indices.dropFirst().reversed() {
|
||||
var flag = false // フラグを初期化する
|
||||
for j in 0 ..< i {
|
||||
if nums[j] > nums[j + 1] {
|
||||
// nums[j] と nums[j + 1] を交換
|
||||
nums.swapAt(j, j + 1)
|
||||
flag = true // 交換する要素を記録
|
||||
}
|
||||
}
|
||||
if !flag { // このバブル処理で要素交換が一度もなければそのまま終了
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@main
|
||||
enum BubbleSort {
|
||||
/* Driver Code */
|
||||
static func main() {
|
||||
var nums = [4, 1, 3, 1, 5, 2]
|
||||
bubbleSort(nums: &nums)
|
||||
print("バブルソート完了後 nums = \(nums)")
|
||||
|
||||
var nums1 = [4, 1, 3, 1, 5, 2]
|
||||
bubbleSortWithFlag(nums: &nums1)
|
||||
print("バブルソート完了後 nums1 = \(nums1)")
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
/**
|
||||
* File: bucket_sort.swift
|
||||
* Created Time: 2023-03-27
|
||||
* Author: nuomi1 (nuomi1@qq.com)
|
||||
*/
|
||||
|
||||
/* バケットソート */
|
||||
func bucketSort(nums: inout [Double]) {
|
||||
// k = n/2 個のバケットを初期化し、各バケットに 2 要素ずつ割り当てる想定とする
|
||||
let k = nums.count / 2
|
||||
var buckets = (0 ..< k).map { _ in [Double]() }
|
||||
// 1. 配列要素を各バケットに振り分ける
|
||||
for num in nums {
|
||||
// 入力データの範囲は [0, 1) であり、num * k を用いてインデックス範囲 [0, k-1] に写像する
|
||||
let i = Int(num * Double(k))
|
||||
// num をバケット i に追加
|
||||
buckets[i].append(num)
|
||||
}
|
||||
// 2. 各バケットをソートする
|
||||
for i in buckets.indices {
|
||||
// 組み込みのソート関数を使う。他のソートアルゴリズムに置き換えてもよい
|
||||
buckets[i].sort()
|
||||
}
|
||||
// 3. バケットを走査して結果を結合
|
||||
var i = nums.startIndex
|
||||
for bucket in buckets {
|
||||
for num in bucket {
|
||||
nums[i] = num
|
||||
i += 1
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@main
|
||||
enum BucketSort {
|
||||
/* Driver Code */
|
||||
static func main() {
|
||||
// 入力データは範囲 [0, 1) の浮動小数点数とする
|
||||
var nums = [0.49, 0.96, 0.82, 0.09, 0.57, 0.43, 0.91, 0.75, 0.15, 0.37]
|
||||
bucketSort(nums: &nums)
|
||||
print("バケットソート完了後 nums = \(nums)")
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
/**
|
||||
* File: counting_sort.swift
|
||||
* Created Time: 2023-03-22
|
||||
* Author: nuomi1 (nuomi1@qq.com)
|
||||
*/
|
||||
|
||||
/* 計数ソート */
|
||||
// 簡易実装のため、オブジェクトのソートには使えない
|
||||
func countingSortNaive(nums: inout [Int]) {
|
||||
// 1. 配列の最大要素 m を求める
|
||||
let m = nums.max()!
|
||||
// 2. 各数値の出現回数を数える
|
||||
// counter[num] は num の出現回数を表す
|
||||
var counter = Array(repeating: 0, count: m + 1)
|
||||
for num in nums {
|
||||
counter[num] += 1
|
||||
}
|
||||
// 3. counter を走査し、各要素を元の配列 nums に書き戻す
|
||||
var i = 0
|
||||
for num in 0 ..< m + 1 {
|
||||
for _ in 0 ..< counter[num] {
|
||||
nums[i] = num
|
||||
i += 1
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* 計数ソート */
|
||||
// 完全な実装で、オブジェクトをソートでき、かつ安定ソートである
|
||||
func countingSort(nums: inout [Int]) {
|
||||
// 1. 配列の最大要素 m を求める
|
||||
let m = nums.max()!
|
||||
// 2. 各数値の出現回数を数える
|
||||
// counter[num] は num の出現回数を表す
|
||||
var counter = Array(repeating: 0, count: m + 1)
|
||||
for num in nums {
|
||||
counter[num] += 1
|
||||
}
|
||||
// 3. counter の累積和を求めて、「出現回数」を「末尾インデックス」に変換する
|
||||
// つまり counter[num]-1 は、num が res に最後に現れるインデックス
|
||||
for i in 0 ..< m {
|
||||
counter[i + 1] += counter[i]
|
||||
}
|
||||
// 4. nums を逆順に走査し、各要素を結果配列 res に格納する
|
||||
// 結果を記録するための配列 res を初期化
|
||||
var res = Array(repeating: 0, count: nums.count)
|
||||
for i in nums.indices.reversed() {
|
||||
let num = nums[i]
|
||||
res[counter[num] - 1] = num // num を対応するインデックスに配置
|
||||
counter[num] -= 1 // 累積和を 1 減らして、次に num を配置するインデックスを得る
|
||||
}
|
||||
// 結果配列 res で元の配列 nums を上書きする
|
||||
for i in nums.indices {
|
||||
nums[i] = res[i]
|
||||
}
|
||||
}
|
||||
|
||||
@main
|
||||
enum CountingSort {
|
||||
/* Driver Code */
|
||||
static func main() {
|
||||
var nums = [1, 0, 1, 2, 0, 4, 0, 2, 2, 4]
|
||||
countingSortNaive(nums: &nums)
|
||||
print("カウントソート(オブジェクトはソート不可)完了後 nums = \(nums)")
|
||||
|
||||
var nums1 = [1, 0, 1, 2, 0, 4, 0, 2, 2, 4]
|
||||
countingSort(nums: &nums1)
|
||||
print("カウントソート完了後 nums1 = \(nums1)")
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
/**
|
||||
* File: heap_sort.swift
|
||||
* Created Time: 2023-05-28
|
||||
* Author: nuomi1 (nuomi1@qq.com)
|
||||
*/
|
||||
|
||||
/* ヒープの長さは n。ノード i から下方向にヒープ化 */
|
||||
func siftDown(nums: inout [Int], n: Int, i: Int) {
|
||||
var i = i
|
||||
while true {
|
||||
// ノード i, l, r のうち値が最大のノードを ma とする
|
||||
let l = 2 * i + 1
|
||||
let r = 2 * i + 2
|
||||
var ma = i
|
||||
if l < n, nums[l] > nums[ma] {
|
||||
ma = l
|
||||
}
|
||||
if r < n, nums[r] > nums[ma] {
|
||||
ma = r
|
||||
}
|
||||
// ノード i が最大、またはインデックス l, r が範囲外なら、ヒープ化は不要なので抜ける
|
||||
if ma == i {
|
||||
break
|
||||
}
|
||||
// 2 つのノードを交換
|
||||
nums.swapAt(i, ma)
|
||||
// ループで上から下へヒープ化
|
||||
i = ma
|
||||
}
|
||||
}
|
||||
|
||||
/* ヒープソート */
|
||||
func heapSort(nums: inout [Int]) {
|
||||
// ヒープ構築:葉ノード以外のすべてのノードをヒープ化する
|
||||
for i in stride(from: nums.count / 2 - 1, through: 0, by: -1) {
|
||||
siftDown(nums: &nums, n: nums.count, i: i)
|
||||
}
|
||||
// ヒープから最大要素を取り出し、n-1 回繰り返す
|
||||
for i in nums.indices.dropFirst().reversed() {
|
||||
// 根ノードと最も右の葉ノードを交換(先頭要素と末尾要素を交換)
|
||||
nums.swapAt(0, i)
|
||||
// 根ノードを起点に、上から下へヒープ化
|
||||
siftDown(nums: &nums, n: i, i: 0)
|
||||
}
|
||||
}
|
||||
|
||||
@main
|
||||
enum HeapSort {
|
||||
/* Driver Code */
|
||||
static func main() {
|
||||
var nums = [4, 1, 3, 1, 5, 2]
|
||||
heapSort(nums: &nums)
|
||||
print("ヒープソート完了後 nums = \(nums)")
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
/**
|
||||
* File: insertion_sort.swift
|
||||
* Created Time: 2023-01-29
|
||||
* Author: nuomi1 (nuomi1@qq.com)
|
||||
*/
|
||||
|
||||
/* 挿入ソート */
|
||||
func insertionSort(nums: inout [Int]) {
|
||||
// 外側ループ:整列済み区間は [0, i-1]
|
||||
for i in nums.indices.dropFirst() {
|
||||
let base = nums[i]
|
||||
var j = i - 1
|
||||
// 内側ループ: base をソート済み区間 [0, i-1] の正しい位置に挿入する
|
||||
while j >= 0, nums[j] > base {
|
||||
nums[j + 1] = nums[j] // nums[j] を 1 つ右へ移動する
|
||||
j -= 1
|
||||
}
|
||||
nums[j + 1] = base // base を正しい位置に配置する
|
||||
}
|
||||
}
|
||||
|
||||
@main
|
||||
enum InsertionSort {
|
||||
/* Driver Code */
|
||||
static func main() {
|
||||
var nums = [4, 1, 3, 1, 5, 2]
|
||||
insertionSort(nums: &nums)
|
||||
print("挿入ソート完了後 nums = \(nums)")
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
/**
|
||||
* File: merge_sort.swift
|
||||
* Created Time: 2023-01-29
|
||||
* Author: nuomi1 (nuomi1@qq.com)
|
||||
*/
|
||||
|
||||
/* 左部分配列と右部分配列をマージ */
|
||||
func merge(nums: inout [Int], left: Int, mid: Int, right: Int) {
|
||||
// 左部分配列の区間は [left, mid]、右部分配列の区間は [mid+1, right]
|
||||
// マージ結果を格納する一時配列 tmp を作成
|
||||
var tmp = Array(repeating: 0, count: right - left + 1)
|
||||
// 左右の部分配列の開始インデックスを初期化する
|
||||
var i = left, j = mid + 1, k = 0
|
||||
// 左右の部分配列にまだ要素がある間は比較し、小さいほうを一時配列にコピーする
|
||||
while i <= mid, j <= right {
|
||||
if nums[i] <= nums[j] {
|
||||
tmp[k] = nums[i]
|
||||
i += 1
|
||||
} else {
|
||||
tmp[k] = nums[j]
|
||||
j += 1
|
||||
}
|
||||
k += 1
|
||||
}
|
||||
// 左右の部分配列の残り要素を一時配列にコピーする
|
||||
while i <= mid {
|
||||
tmp[k] = nums[i]
|
||||
i += 1
|
||||
k += 1
|
||||
}
|
||||
while j <= right {
|
||||
tmp[k] = nums[j]
|
||||
j += 1
|
||||
k += 1
|
||||
}
|
||||
// 一時配列 tmp の要素を元の配列 nums の対応区間にコピーする
|
||||
for k in tmp.indices {
|
||||
nums[left + k] = tmp[k]
|
||||
}
|
||||
}
|
||||
|
||||
/* マージソート */
|
||||
func mergeSort(nums: inout [Int], left: Int, right: Int) {
|
||||
// 終了条件
|
||||
if left >= right { // 部分配列の長さが 1 になったら再帰を終了
|
||||
return
|
||||
}
|
||||
// 分割フェーズ
|
||||
let mid = left + (right - left) / 2 // 中点を計算
|
||||
mergeSort(nums: &nums, left: left, right: mid) // 左部分配列を再帰処理
|
||||
mergeSort(nums: &nums, left: mid + 1, right: right) // 右部分配列を再帰処理
|
||||
// マージフェーズ
|
||||
merge(nums: &nums, left: left, mid: mid, right: right)
|
||||
}
|
||||
|
||||
@main
|
||||
enum MergeSort {
|
||||
/* Driver Code */
|
||||
static func main() {
|
||||
/* マージソート */
|
||||
var nums = [7, 3, 2, 6, 0, 1, 5, 4]
|
||||
mergeSort(nums: &nums, left: nums.startIndex, right: nums.endIndex - 1)
|
||||
print("マージソート完了後 nums = \(nums)")
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,114 @@
|
||||
/**
|
||||
* File: quick_sort.swift
|
||||
* Created Time: 2023-01-29
|
||||
* Author: nuomi1 (nuomi1@qq.com)
|
||||
*/
|
||||
|
||||
/* クイックソートクラス */
|
||||
/* 番兵分割 */
|
||||
func partition(nums: inout [Int], left: Int, right: Int) -> Int {
|
||||
// nums[left] を基準値とする
|
||||
var i = left
|
||||
var j = right
|
||||
while i < j {
|
||||
while i < j, nums[j] >= nums[left] {
|
||||
j -= 1 // 右から左へ基準値未満の最初の要素を探す
|
||||
}
|
||||
while i < j, nums[i] <= nums[left] {
|
||||
i += 1 // 左から右へ基準値より大きい最初の要素を探す
|
||||
}
|
||||
nums.swapAt(i, j) // この 2 つの要素を交換
|
||||
}
|
||||
nums.swapAt(i, left) // 基準値を 2 つの部分配列の境界へ交換する
|
||||
return i // 基準値のインデックスを返す
|
||||
}
|
||||
|
||||
/* クイックソート */
|
||||
func quickSort(nums: inout [Int], left: Int, right: Int) {
|
||||
// 部分配列の長さが 1 なら再帰を終了する
|
||||
if left >= right {
|
||||
return
|
||||
}
|
||||
// 番兵分割
|
||||
let pivot = partition(nums: &nums, left: left, right: right)
|
||||
// 左右の部分配列を再帰処理
|
||||
quickSort(nums: &nums, left: left, right: pivot - 1)
|
||||
quickSort(nums: &nums, left: pivot + 1, right: right)
|
||||
}
|
||||
|
||||
/* クイックソートクラス(中央値ピボット最適化) */
|
||||
/* 3つの候補要素の中央値を選ぶ */
|
||||
func medianThree(nums: [Int], left: Int, mid: Int, right: Int) -> Int {
|
||||
let l = nums[left]
|
||||
let m = nums[mid]
|
||||
let r = nums[right]
|
||||
if (l <= m && m <= r) || (r <= m && m <= l) {
|
||||
return mid // m は l と r の間
|
||||
}
|
||||
if (m <= l && l <= r) || (r <= l && l <= m) {
|
||||
return left // l は m と r の間
|
||||
}
|
||||
return right
|
||||
}
|
||||
|
||||
/* 番兵による分割処理(3 点中央値) */
|
||||
func partitionMedian(nums: inout [Int], left: Int, right: Int) -> Int {
|
||||
// 3つの候補要素の中央値を選ぶ
|
||||
let med = medianThree(nums: nums, left: left, mid: left + (right - left) / 2, right: right)
|
||||
// 中央値を配列の最左端に交換する
|
||||
nums.swapAt(left, med)
|
||||
return partition(nums: &nums, left: left, right: right)
|
||||
}
|
||||
|
||||
/* クイックソート(中央値の基準値で最適化) */
|
||||
func quickSortMedian(nums: inout [Int], left: Int, right: Int) {
|
||||
// 部分配列の長さが 1 なら再帰を終了する
|
||||
if left >= right {
|
||||
return
|
||||
}
|
||||
// 番兵分割
|
||||
let pivot = partitionMedian(nums: &nums, left: left, right: right)
|
||||
// 左右の部分配列を再帰処理
|
||||
quickSortMedian(nums: &nums, left: left, right: pivot - 1)
|
||||
quickSortMedian(nums: &nums, left: pivot + 1, right: right)
|
||||
}
|
||||
|
||||
/* クイックソート(再帰深度最適化) */
|
||||
func quickSortTailCall(nums: inout [Int], left: Int, right: Int) {
|
||||
var left = left
|
||||
var right = right
|
||||
// 部分配列の長さが 1 なら終了
|
||||
while left < right {
|
||||
// 番兵による分割処理
|
||||
let pivot = partition(nums: &nums, left: left, right: right)
|
||||
// 2 つの部分配列のうち短いほうにクイックソートを適用する
|
||||
if (pivot - left) < (right - pivot) {
|
||||
quickSortTailCall(nums: &nums, left: left, right: pivot - 1) // 左部分配列を再帰的にソート
|
||||
left = pivot + 1 // 未ソート区間の残りは [pivot + 1, right]
|
||||
} else {
|
||||
quickSortTailCall(nums: &nums, left: pivot + 1, right: right) // 右部分配列を再帰的にソート
|
||||
right = pivot - 1 // 未ソート区間の残りは [left, pivot - 1]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@main
|
||||
enum QuickSort {
|
||||
/* Driver Code */
|
||||
static func main() {
|
||||
/* クイックソート */
|
||||
var nums = [2, 4, 1, 0, 3, 5]
|
||||
quickSort(nums: &nums, left: nums.startIndex, right: nums.endIndex - 1)
|
||||
print("クイックソート完了後 nums = \(nums)")
|
||||
|
||||
/* クイックソート(中央値の基準値で最適化) */
|
||||
var nums1 = [2, 4, 1, 0, 3, 5]
|
||||
quickSortMedian(nums: &nums1, left: nums1.startIndex, right: nums1.endIndex - 1)
|
||||
print("クイックソート(中央値ピボット最適化)完了後 nums1 = \(nums1)")
|
||||
|
||||
/* クイックソート(再帰深度最適化) */
|
||||
var nums2 = [2, 4, 1, 0, 3, 5]
|
||||
quickSortTailCall(nums: &nums2, left: nums2.startIndex, right: nums2.endIndex - 1)
|
||||
print("クイックソート(再帰深度最適化)完了後 nums2 = \(nums2)")
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,79 @@
|
||||
/**
|
||||
* File: radix_sort.swift
|
||||
* Created Time: 2023-01-29
|
||||
* Author: nuomi1 (nuomi1@qq.com)
|
||||
*/
|
||||
|
||||
/* 要素 num の下から k 桁目を取得(exp = 10^(k-1)) */
|
||||
func digit(num: Int, exp: Int) -> Int {
|
||||
// ここで高コストな累乗計算を繰り返さないよう、k ではなく exp を渡す
|
||||
(num / exp) % 10
|
||||
}
|
||||
|
||||
/* 計数ソート(nums の k 桁目でソート) */
|
||||
func countingSortDigit(nums: inout [Int], exp: Int) {
|
||||
// 10 進数の各桁は 0~9 の範囲なので、長さ 10 のバケット配列が必要
|
||||
var counter = Array(repeating: 0, count: 10)
|
||||
// 0~9 の各数字の出現回数を集計する
|
||||
for i in nums.indices {
|
||||
let d = digit(num: nums[i], exp: exp) // nums[i] の第 k 位を取得し、d とする
|
||||
counter[d] += 1 // 数字 d の出現回数を数える
|
||||
}
|
||||
// 累積和を求め、「出現回数」を「配列インデックス」に変換する
|
||||
for i in 1 ..< 10 {
|
||||
counter[i] += counter[i - 1]
|
||||
}
|
||||
// 逆順に走査し、バケット内の集計結果に従って各要素を res に格納する
|
||||
var res = Array(repeating: 0, count: nums.count)
|
||||
for i in nums.indices.reversed() {
|
||||
let d = digit(num: nums[i], exp: exp)
|
||||
let j = counter[d] - 1 // d の配列内インデックス j を取得する
|
||||
res[j] = nums[i] // 現在の要素をインデックス j に格納する
|
||||
counter[d] -= 1 // d の個数を 1 減らす
|
||||
}
|
||||
// 結果で元の配列 nums を上書きする
|
||||
for i in nums.indices {
|
||||
nums[i] = res[i]
|
||||
}
|
||||
}
|
||||
|
||||
/* 基数ソート */
|
||||
func radixSort(nums: inout [Int]) {
|
||||
// 最大桁数の判定用に配列の最大要素を取得
|
||||
var m = Int.min
|
||||
for num in nums {
|
||||
if num > m {
|
||||
m = num
|
||||
}
|
||||
}
|
||||
// 下位桁から上位桁の順に走査する
|
||||
for exp in sequence(first: 1, next: { m >= ($0 * 10) ? $0 * 10 : nil }) {
|
||||
// 配列要素の k 桁目に対して計数ソートを行う
|
||||
// k = 1 -> exp = 1
|
||||
// k = 2 -> exp = 10
|
||||
// つまり exp = 10^(k-1)
|
||||
countingSortDigit(nums: &nums, exp: exp)
|
||||
}
|
||||
}
|
||||
|
||||
@main
|
||||
enum RadixSort {
|
||||
/* Driver Code */
|
||||
static func main() {
|
||||
// 基数ソート
|
||||
var nums = [
|
||||
10_546_151,
|
||||
35_663_510,
|
||||
42_865_989,
|
||||
34_862_445,
|
||||
81_883_077,
|
||||
88_906_420,
|
||||
72_429_244,
|
||||
30_524_779,
|
||||
82_060_337,
|
||||
63_832_996,
|
||||
]
|
||||
radixSort(nums: &nums)
|
||||
print("基数ソート完了後 nums = \(nums)")
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
/**
|
||||
* File: selection_sort.swift
|
||||
* Created Time: 2023-05-28
|
||||
* Author: nuomi1 (nuomi1@qq.com)
|
||||
*/
|
||||
|
||||
/* 選択ソート */
|
||||
func selectionSort(nums: inout [Int]) {
|
||||
// 外側ループ:未整列区間は [i, n-1]
|
||||
for i in nums.indices.dropLast() {
|
||||
// 内側のループ:未ソート区間の最小要素を見つける
|
||||
var k = i
|
||||
for j in nums.indices.dropFirst(i + 1) {
|
||||
if nums[j] < nums[k] {
|
||||
k = j // 最小要素のインデックスを記録
|
||||
}
|
||||
}
|
||||
// その最小要素を未整列区間の先頭要素と交換する
|
||||
nums.swapAt(i, k)
|
||||
}
|
||||
}
|
||||
|
||||
@main
|
||||
enum SelectionSort {
|
||||
/* Driver Code */
|
||||
static func main() {
|
||||
var nums = [4, 1, 3, 1, 5, 2]
|
||||
selectionSort(nums: &nums)
|
||||
print("選択ソート完了後 nums = \(nums)")
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,148 @@
|
||||
/**
|
||||
* File: array_deque.swift
|
||||
* Created Time: 2023-02-22
|
||||
* Author: nuomi1 (nuomi1@qq.com)
|
||||
*/
|
||||
|
||||
/* 循環配列ベースの両端キュー */
|
||||
class ArrayDeque {
|
||||
private var nums: [Int] // 両端キューの要素を格納する配列
|
||||
private var front: Int // 先頭ポインタ。先頭要素を指す
|
||||
private var _size: Int // 両端キューの長さ
|
||||
|
||||
/* コンストラクタ */
|
||||
init(capacity: Int) {
|
||||
nums = Array(repeating: 0, count: capacity)
|
||||
front = 0
|
||||
_size = 0
|
||||
}
|
||||
|
||||
/* 両端キューの容量を取得 */
|
||||
func capacity() -> Int {
|
||||
nums.count
|
||||
}
|
||||
|
||||
/* 両端キューの長さを取得 */
|
||||
func size() -> Int {
|
||||
_size
|
||||
}
|
||||
|
||||
/* 両端キューが空かどうかを判定 */
|
||||
func isEmpty() -> Bool {
|
||||
size() == 0
|
||||
}
|
||||
|
||||
/* 循環配列のインデックスを計算 */
|
||||
private func index(i: Int) -> Int {
|
||||
// 剰余演算により配列の先頭と末尾をつなげる
|
||||
// i が配列の末尾を越えたら先頭に戻る
|
||||
// i が配列の先頭を越えて前に出たら末尾に戻る
|
||||
(i + capacity()) % capacity()
|
||||
}
|
||||
|
||||
/* キュー先頭にエンキュー */
|
||||
func pushFirst(num: Int) {
|
||||
if size() == capacity() {
|
||||
print("両端キューがいっぱいです")
|
||||
return
|
||||
}
|
||||
// 先頭ポインタを左に 1 つ移動する
|
||||
// 剰余演算により、front が配列先頭を越えた後に末尾へ戻るようにする
|
||||
front = index(i: front - 1)
|
||||
// num をキュー先頭に追加
|
||||
nums[front] = num
|
||||
_size += 1
|
||||
}
|
||||
|
||||
/* キュー末尾にエンキュー */
|
||||
func pushLast(num: Int) {
|
||||
if size() == capacity() {
|
||||
print("両端キューがいっぱいです")
|
||||
return
|
||||
}
|
||||
// キュー末尾ポインタを計算し、末尾インデックス + 1 を指す
|
||||
let rear = index(i: front + size())
|
||||
// num をキュー末尾に追加
|
||||
nums[rear] = num
|
||||
_size += 1
|
||||
}
|
||||
|
||||
/* キュー先頭からデキュー */
|
||||
func popFirst() -> Int {
|
||||
let num = peekFirst()
|
||||
// 先頭ポインタを 1 つ後ろへ進める
|
||||
front = index(i: front + 1)
|
||||
_size -= 1
|
||||
return num
|
||||
}
|
||||
|
||||
/* キュー末尾からデキュー */
|
||||
func popLast() -> Int {
|
||||
let num = peekLast()
|
||||
_size -= 1
|
||||
return num
|
||||
}
|
||||
|
||||
/* キュー先頭の要素にアクセス */
|
||||
func peekFirst() -> Int {
|
||||
if isEmpty() {
|
||||
fatalError("両端キューが空です")
|
||||
}
|
||||
return nums[front]
|
||||
}
|
||||
|
||||
/* キュー末尾の要素にアクセス */
|
||||
func peekLast() -> Int {
|
||||
if isEmpty() {
|
||||
fatalError("両端キューが空です")
|
||||
}
|
||||
// 末尾要素のインデックスを計算
|
||||
let last = index(i: front + size() - 1)
|
||||
return nums[last]
|
||||
}
|
||||
|
||||
/* 出力用の配列を返す */
|
||||
func toArray() -> [Int] {
|
||||
// 有効長の範囲内のリスト要素のみを変換
|
||||
(front ..< front + size()).map { nums[index(i: $0)] }
|
||||
}
|
||||
}
|
||||
|
||||
@main
|
||||
enum _ArrayDeque {
|
||||
/* Driver Code */
|
||||
static func main() {
|
||||
/* 両端キューを初期化 */
|
||||
let deque = ArrayDeque(capacity: 10)
|
||||
deque.pushLast(num: 3)
|
||||
deque.pushLast(num: 2)
|
||||
deque.pushLast(num: 5)
|
||||
print("両端キュー deque = \(deque.toArray())")
|
||||
|
||||
/* 要素にアクセス */
|
||||
let peekFirst = deque.peekFirst()
|
||||
print("先頭要素 peekFirst = \(peekFirst)")
|
||||
let peekLast = deque.peekLast()
|
||||
print("末尾要素 peekLast = \(peekLast)")
|
||||
|
||||
/* 要素をエンキュー */
|
||||
deque.pushLast(num: 4)
|
||||
print("要素 4 を末尾に追加した後 deque = \(deque.toArray())")
|
||||
deque.pushFirst(num: 1)
|
||||
print("要素 1 を先頭に追加した後 deque = \(deque.toArray())")
|
||||
|
||||
/* 要素をデキュー */
|
||||
let popLast = deque.popLast()
|
||||
print("末尾から取り出した要素 = \(popLast),末尾から取り出した後 deque = \(deque.toArray())")
|
||||
let popFirst = deque.popFirst()
|
||||
print("先頭から取り出した要素 = \(popFirst),先頭から取り出した後 deque = \(deque.toArray())")
|
||||
|
||||
/* 両端キューの長さを取得 */
|
||||
let size = deque.size()
|
||||
print("両端キューのサイズ size = \(size)")
|
||||
|
||||
/* 両端キューが空かどうかを判定 */
|
||||
let isEmpty = deque.isEmpty()
|
||||
print("両端キューが空かどうか = \(isEmpty)")
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,113 @@
|
||||
/**
|
||||
* File: array_queue.swift
|
||||
* Created Time: 2023-01-11
|
||||
* Author: nuomi1 (nuomi1@qq.com)
|
||||
*/
|
||||
|
||||
/* 循環配列ベースのキュー */
|
||||
class ArrayQueue {
|
||||
private var nums: [Int] // キュー要素を格納する配列
|
||||
private var front: Int // 先頭ポインタ。先頭要素を指す
|
||||
private var _size: Int // キューの長さ
|
||||
|
||||
init(capacity: Int) {
|
||||
// 配列を初期化
|
||||
nums = Array(repeating: 0, count: capacity)
|
||||
front = 0
|
||||
_size = 0
|
||||
}
|
||||
|
||||
/* キューの容量を取得 */
|
||||
func capacity() -> Int {
|
||||
nums.count
|
||||
}
|
||||
|
||||
/* キューの長さを取得 */
|
||||
func size() -> Int {
|
||||
_size
|
||||
}
|
||||
|
||||
/* キューが空かどうかを判定 */
|
||||
func isEmpty() -> Bool {
|
||||
size() == 0
|
||||
}
|
||||
|
||||
/* エンキュー */
|
||||
func push(num: Int) {
|
||||
if size() == capacity() {
|
||||
print("キューがいっぱいです")
|
||||
return
|
||||
}
|
||||
// 末尾ポインタを計算し、末尾インデックス + 1 を指す
|
||||
// 剰余演算により、rear が配列末尾を越えた後に先頭へ戻るようにする
|
||||
let rear = (front + size()) % capacity()
|
||||
// num をキュー末尾に追加
|
||||
nums[rear] = num
|
||||
_size += 1
|
||||
}
|
||||
|
||||
/* デキュー */
|
||||
@discardableResult
|
||||
func pop() -> Int {
|
||||
let num = peek()
|
||||
// 先頭ポインタを1つ後ろへ進め、末尾を越えたら配列先頭に戻す
|
||||
front = (front + 1) % capacity()
|
||||
_size -= 1
|
||||
return num
|
||||
}
|
||||
|
||||
/* キュー先頭の要素にアクセス */
|
||||
func peek() -> Int {
|
||||
if isEmpty() {
|
||||
fatalError("キューが空です")
|
||||
}
|
||||
return nums[front]
|
||||
}
|
||||
|
||||
/* 配列を返す */
|
||||
func toArray() -> [Int] {
|
||||
// 有効長の範囲内のリスト要素のみを変換
|
||||
(front ..< front + size()).map { nums[$0 % capacity()] }
|
||||
}
|
||||
}
|
||||
|
||||
@main
|
||||
enum _ArrayQueue {
|
||||
/* Driver Code */
|
||||
static func main() {
|
||||
/* キューを初期化 */
|
||||
let capacity = 10
|
||||
let queue = ArrayQueue(capacity: capacity)
|
||||
|
||||
/* 要素をエンキュー */
|
||||
queue.push(num: 1)
|
||||
queue.push(num: 3)
|
||||
queue.push(num: 2)
|
||||
queue.push(num: 5)
|
||||
queue.push(num: 4)
|
||||
print("キュー queue = \(queue.toArray())")
|
||||
|
||||
/* キュー先頭の要素にアクセス */
|
||||
let peek = queue.peek()
|
||||
print("先頭要素 peek = \(peek)")
|
||||
|
||||
/* 要素をデキュー */
|
||||
let pop = queue.pop()
|
||||
print("取り出した要素 pop = \(pop),取り出し後 queue = \(queue.toArray())")
|
||||
|
||||
/* キューの長さを取得 */
|
||||
let size = queue.size()
|
||||
print("キューのサイズ size = \(size)")
|
||||
|
||||
/* キューが空かどうかを判定 */
|
||||
let isEmpty = queue.isEmpty()
|
||||
print("キューが空かどうか = \(isEmpty)")
|
||||
|
||||
/* 循環配列をテストする */
|
||||
for i in 0 ..< 10 {
|
||||
queue.push(num: i)
|
||||
queue.pop()
|
||||
print("第 \(i) 回のエンキュー + デキュー後 queue = \(queue.toArray())")
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,85 @@
|
||||
/**
|
||||
* File: array_stack.swift
|
||||
* Created Time: 2023-01-09
|
||||
* Author: nuomi1 (nuomi1@qq.com)
|
||||
*/
|
||||
|
||||
/* 配列ベースのスタック */
|
||||
class ArrayStack {
|
||||
private var stack: [Int]
|
||||
|
||||
init() {
|
||||
// リスト(動的配列)を初期化する
|
||||
stack = []
|
||||
}
|
||||
|
||||
/* スタックの長さを取得 */
|
||||
func size() -> Int {
|
||||
stack.count
|
||||
}
|
||||
|
||||
/* スタックが空かどうかを判定 */
|
||||
func isEmpty() -> Bool {
|
||||
stack.isEmpty
|
||||
}
|
||||
|
||||
/* プッシュ */
|
||||
func push(num: Int) {
|
||||
stack.append(num)
|
||||
}
|
||||
|
||||
/* ポップ */
|
||||
@discardableResult
|
||||
func pop() -> Int {
|
||||
if isEmpty() {
|
||||
fatalError("スタックが空です")
|
||||
}
|
||||
return stack.removeLast()
|
||||
}
|
||||
|
||||
/* スタックトップの要素にアクセス */
|
||||
func peek() -> Int {
|
||||
if isEmpty() {
|
||||
fatalError("スタックが空です")
|
||||
}
|
||||
return stack.last!
|
||||
}
|
||||
|
||||
/* List を Array に変換して返す */
|
||||
func toArray() -> [Int] {
|
||||
stack
|
||||
}
|
||||
}
|
||||
|
||||
@main
|
||||
enum _ArrayStack {
|
||||
/* Driver Code */
|
||||
static func main() {
|
||||
/* スタックを初期化 */
|
||||
let stack = ArrayStack()
|
||||
|
||||
/* 要素をプッシュ */
|
||||
stack.push(num: 1)
|
||||
stack.push(num: 3)
|
||||
stack.push(num: 2)
|
||||
stack.push(num: 5)
|
||||
stack.push(num: 4)
|
||||
print("スタック stack = \(stack.toArray())")
|
||||
|
||||
/* スタックトップの要素にアクセス */
|
||||
let peek = stack.peek()
|
||||
print("スタックトップ要素 peek = \(peek)")
|
||||
|
||||
/* 要素をポップ */
|
||||
let pop = stack.pop()
|
||||
print("ポップした要素 pop = \(pop)、ポップ後の stack = \(stack.toArray())")
|
||||
|
||||
/* スタックの長さを取得 */
|
||||
let size = stack.size()
|
||||
print("スタックの長さ size = \(size)")
|
||||
|
||||
/* 空かどうかを判定 */
|
||||
let isEmpty = stack.isEmpty()
|
||||
print("スタックが空かどうか = \(isEmpty)")
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
/**
|
||||
* File: deque.swift
|
||||
* Created Time: 2023-01-14
|
||||
* Author: nuomi1 (nuomi1@qq.com)
|
||||
*/
|
||||
|
||||
@main
|
||||
enum Deque {
|
||||
/* Driver Code */
|
||||
static func main() {
|
||||
/* 両端キューを初期化 */
|
||||
// Swift には組み込みの両端キュークラスがないため、Array を両端キューとして使う
|
||||
var deque: [Int] = []
|
||||
|
||||
/* 要素をエンキュー */
|
||||
deque.append(2)
|
||||
deque.append(5)
|
||||
deque.append(4)
|
||||
deque.insert(3, at: 0)
|
||||
deque.insert(1, at: 0)
|
||||
print("両端キュー deque = \(deque)")
|
||||
|
||||
/* 要素にアクセス */
|
||||
let peekFirst = deque.first!
|
||||
print("先頭要素 peekFirst = \(peekFirst)")
|
||||
let peekLast = deque.last!
|
||||
print("末尾要素 peekLast = \(peekLast)")
|
||||
|
||||
/* 要素をデキュー */
|
||||
// Array を用いる場合、popFirst の計算量は O(n)
|
||||
let popFirst = deque.removeFirst()
|
||||
print("先頭からデキューした要素 popFirst = \(popFirst)、先頭からデキュー後の deque = \(deque)")
|
||||
let popLast = deque.removeLast()
|
||||
print("末尾からデキューした要素 popLast = \(popLast)、末尾からデキュー後の deque = \(deque)")
|
||||
|
||||
/* 両端キューの長さを取得 */
|
||||
let size = deque.count
|
||||
print("両端キューのサイズ size = \(size)")
|
||||
|
||||
/* 両端キューが空かどうかを判定 */
|
||||
let isEmpty = deque.isEmpty
|
||||
print("両端キューが空かどうか = \(isEmpty)")
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,180 @@
|
||||
/**
|
||||
* File: linkedlist_deque.swift
|
||||
* Created Time: 2023-02-22
|
||||
* Author: nuomi1 (nuomi1@qq.com)
|
||||
*/
|
||||
|
||||
/* 双方向連結リストノード */
|
||||
class ListNode {
|
||||
var val: Int // ノード値
|
||||
var next: ListNode? // 後続ノードへの参照
|
||||
weak var prev: ListNode? // 前駆ノードへの参照
|
||||
|
||||
init(val: Int) {
|
||||
self.val = val
|
||||
}
|
||||
}
|
||||
|
||||
/* 双方向連結リストベースの両端キュー */
|
||||
class LinkedListDeque {
|
||||
private var front: ListNode? // 先頭ノード front
|
||||
private var rear: ListNode? // 末尾ノード rear
|
||||
private var _size: Int // 両端キューの長さ
|
||||
|
||||
init() {
|
||||
_size = 0
|
||||
}
|
||||
|
||||
/* 両端キューの長さを取得 */
|
||||
func size() -> Int {
|
||||
_size
|
||||
}
|
||||
|
||||
/* 両端キューが空かどうかを判定 */
|
||||
func isEmpty() -> Bool {
|
||||
size() == 0
|
||||
}
|
||||
|
||||
/* エンキュー操作 */
|
||||
private func push(num: Int, isFront: Bool) {
|
||||
let node = ListNode(val: num)
|
||||
// 連結リストが空なら、front と rear の両方を node に向ける
|
||||
if isEmpty() {
|
||||
front = node
|
||||
rear = node
|
||||
}
|
||||
// 先頭へのエンキュー操作
|
||||
else if isFront {
|
||||
// node を連結リストの先頭に追加
|
||||
front?.prev = node
|
||||
node.next = front
|
||||
front = node // 先頭ノードを更新する
|
||||
}
|
||||
// 末尾へのエンキュー操作
|
||||
else {
|
||||
// node を連結リストの末尾に追加
|
||||
rear?.next = node
|
||||
node.prev = rear
|
||||
rear = node // 末尾ノードを更新する
|
||||
}
|
||||
_size += 1 // キューの長さを更新
|
||||
}
|
||||
|
||||
/* キュー先頭にエンキュー */
|
||||
func pushFirst(num: Int) {
|
||||
push(num: num, isFront: true)
|
||||
}
|
||||
|
||||
/* キュー末尾にエンキュー */
|
||||
func pushLast(num: Int) {
|
||||
push(num: num, isFront: false)
|
||||
}
|
||||
|
||||
/* デキュー操作 */
|
||||
private func pop(isFront: Bool) -> Int {
|
||||
if isEmpty() {
|
||||
fatalError("両端キューが空です")
|
||||
}
|
||||
let val: Int
|
||||
// キュー先頭からの取り出し
|
||||
if isFront {
|
||||
val = front!.val // 先頭ノードの値を一時保存
|
||||
// 先頭ノードを削除
|
||||
let fNext = front?.next
|
||||
if fNext != nil {
|
||||
fNext?.prev = nil
|
||||
front?.next = nil
|
||||
}
|
||||
front = fNext // 先頭ノードを更新する
|
||||
}
|
||||
// キュー末尾からの取り出し
|
||||
else {
|
||||
val = rear!.val // 末尾ノードの値を一時保存
|
||||
// 末尾ノードを削除
|
||||
let rPrev = rear?.prev
|
||||
if rPrev != nil {
|
||||
rPrev?.next = nil
|
||||
rear?.prev = nil
|
||||
}
|
||||
rear = rPrev // 末尾ノードを更新する
|
||||
}
|
||||
_size -= 1 // キューの長さを更新
|
||||
return val
|
||||
}
|
||||
|
||||
/* キュー先頭からデキュー */
|
||||
func popFirst() -> Int {
|
||||
pop(isFront: true)
|
||||
}
|
||||
|
||||
/* キュー末尾からデキュー */
|
||||
func popLast() -> Int {
|
||||
pop(isFront: false)
|
||||
}
|
||||
|
||||
/* キュー先頭の要素にアクセス */
|
||||
func peekFirst() -> Int {
|
||||
if isEmpty() {
|
||||
fatalError("両端キューが空です")
|
||||
}
|
||||
return front!.val
|
||||
}
|
||||
|
||||
/* キュー末尾の要素にアクセス */
|
||||
func peekLast() -> Int {
|
||||
if isEmpty() {
|
||||
fatalError("両端キューが空です")
|
||||
}
|
||||
return rear!.val
|
||||
}
|
||||
|
||||
/* 出力用の配列を返す */
|
||||
func toArray() -> [Int] {
|
||||
var node = front
|
||||
var res = Array(repeating: 0, count: size())
|
||||
for i in res.indices {
|
||||
res[i] = node!.val
|
||||
node = node?.next
|
||||
}
|
||||
return res
|
||||
}
|
||||
}
|
||||
|
||||
@main
|
||||
enum _LinkedListDeque {
|
||||
/* Driver Code */
|
||||
static func main() {
|
||||
/* 両端キューを初期化 */
|
||||
let deque = LinkedListDeque()
|
||||
deque.pushLast(num: 3)
|
||||
deque.pushLast(num: 2)
|
||||
deque.pushLast(num: 5)
|
||||
print("両端キュー deque = \(deque.toArray())")
|
||||
|
||||
/* 要素にアクセス */
|
||||
let peekFirst = deque.peekFirst()
|
||||
print("先頭要素 peekFirst = \(peekFirst)")
|
||||
let peekLast = deque.peekLast()
|
||||
print("末尾要素 peekLast = \(peekLast)")
|
||||
|
||||
/* 要素をエンキュー */
|
||||
deque.pushLast(num: 4)
|
||||
print("要素 4 を末尾に追加した後 deque = \(deque.toArray())")
|
||||
deque.pushFirst(num: 1)
|
||||
print("要素 1 を先頭に追加した後 deque = \(deque.toArray())")
|
||||
|
||||
/* 要素をデキュー */
|
||||
let popLast = deque.popLast()
|
||||
print("末尾から取り出した要素 = \(popLast),末尾から取り出した後 deque = \(deque.toArray())")
|
||||
let popFirst = deque.popFirst()
|
||||
print("先頭から取り出した要素 = \(popFirst),先頭から取り出した後 deque = \(deque.toArray())")
|
||||
|
||||
/* 両端キューの長さを取得 */
|
||||
let size = deque.size()
|
||||
print("両端キューのサイズ size = \(size)")
|
||||
|
||||
/* 両端キューが空かどうかを判定 */
|
||||
let isEmpty = deque.isEmpty()
|
||||
print("両端キューが空かどうか = \(isEmpty)")
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,107 @@
|
||||
/**
|
||||
* File: linkedlist_queue.swift
|
||||
* Created Time: 2023-01-11
|
||||
* Author: nuomi1 (nuomi1@qq.com)
|
||||
*/
|
||||
|
||||
import utils
|
||||
|
||||
/* 連結リストベースのキュー */
|
||||
class LinkedListQueue {
|
||||
private var front: ListNode? // 先頭ノード
|
||||
private var rear: ListNode? // 末尾ノード
|
||||
private var _size: Int
|
||||
|
||||
init() {
|
||||
_size = 0
|
||||
}
|
||||
|
||||
/* キューの長さを取得 */
|
||||
func size() -> Int {
|
||||
_size
|
||||
}
|
||||
|
||||
/* キューが空かどうかを判定 */
|
||||
func isEmpty() -> Bool {
|
||||
size() == 0
|
||||
}
|
||||
|
||||
/* エンキュー */
|
||||
func push(num: Int) {
|
||||
// 末尾ノードの後ろに num を追加
|
||||
let node = ListNode(x: num)
|
||||
// キューが空なら、先頭・末尾ノードをともにそのノードに設定
|
||||
if front == nil {
|
||||
front = node
|
||||
rear = node
|
||||
}
|
||||
// キューが空でなければ、そのノードを末尾ノードの後ろに追加
|
||||
else {
|
||||
rear?.next = node
|
||||
rear = node
|
||||
}
|
||||
_size += 1
|
||||
}
|
||||
|
||||
/* デキュー */
|
||||
@discardableResult
|
||||
func pop() -> Int {
|
||||
let num = peek()
|
||||
// 先頭ノードを削除
|
||||
front = front?.next
|
||||
_size -= 1
|
||||
return num
|
||||
}
|
||||
|
||||
/* キュー先頭の要素にアクセス */
|
||||
func peek() -> Int {
|
||||
if isEmpty() {
|
||||
fatalError("キューが空です")
|
||||
}
|
||||
return front!.val
|
||||
}
|
||||
|
||||
/* 連結リストを Array に変換して返す */
|
||||
func toArray() -> [Int] {
|
||||
var node = front
|
||||
var res = Array(repeating: 0, count: size())
|
||||
for i in res.indices {
|
||||
res[i] = node!.val
|
||||
node = node?.next
|
||||
}
|
||||
return res
|
||||
}
|
||||
}
|
||||
|
||||
@main
|
||||
enum _LinkedListQueue {
|
||||
/* Driver Code */
|
||||
static func main() {
|
||||
/* キューを初期化 */
|
||||
let queue = LinkedListQueue()
|
||||
|
||||
/* 要素をエンキュー */
|
||||
queue.push(num: 1)
|
||||
queue.push(num: 3)
|
||||
queue.push(num: 2)
|
||||
queue.push(num: 5)
|
||||
queue.push(num: 4)
|
||||
print("キュー queue = \(queue.toArray())")
|
||||
|
||||
/* キュー先頭の要素にアクセス */
|
||||
let peek = queue.peek()
|
||||
print("先頭要素 peek = \(peek)")
|
||||
|
||||
/* 要素をデキュー */
|
||||
let pop = queue.pop()
|
||||
print("取り出した要素 pop = \(pop),取り出し後 queue = \(queue.toArray())")
|
||||
|
||||
/* キューの長さを取得 */
|
||||
let size = queue.size()
|
||||
print("キューのサイズ size = \(size)")
|
||||
|
||||
/* キューが空かどうかを判定 */
|
||||
let isEmpty = queue.isEmpty()
|
||||
print("キューが空かどうか = \(isEmpty)")
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,96 @@
|
||||
/**
|
||||
* File: linkedlist_stack.swift
|
||||
* Created Time: 2023-01-09
|
||||
* Author: nuomi1 (nuomi1@qq.com)
|
||||
*/
|
||||
|
||||
import utils
|
||||
|
||||
/* 連結リストベースのスタック */
|
||||
class LinkedListStack {
|
||||
private var _peek: ListNode? // 先頭ノードをスタックトップとする
|
||||
private var _size: Int // スタックの長さ
|
||||
|
||||
init() {
|
||||
_size = 0
|
||||
}
|
||||
|
||||
/* スタックの長さを取得 */
|
||||
func size() -> Int {
|
||||
_size
|
||||
}
|
||||
|
||||
/* スタックが空かどうかを判定 */
|
||||
func isEmpty() -> Bool {
|
||||
size() == 0
|
||||
}
|
||||
|
||||
/* プッシュ */
|
||||
func push(num: Int) {
|
||||
let node = ListNode(x: num)
|
||||
node.next = _peek
|
||||
_peek = node
|
||||
_size += 1
|
||||
}
|
||||
|
||||
/* ポップ */
|
||||
@discardableResult
|
||||
func pop() -> Int {
|
||||
let num = peek()
|
||||
_peek = _peek?.next
|
||||
_size -= 1
|
||||
return num
|
||||
}
|
||||
|
||||
/* スタックトップの要素にアクセス */
|
||||
func peek() -> Int {
|
||||
if isEmpty() {
|
||||
fatalError("スタックが空です")
|
||||
}
|
||||
return _peek!.val
|
||||
}
|
||||
|
||||
/* List を Array に変換して返す */
|
||||
func toArray() -> [Int] {
|
||||
var node = _peek
|
||||
var res = Array(repeating: 0, count: size())
|
||||
for i in res.indices.reversed() {
|
||||
res[i] = node!.val
|
||||
node = node?.next
|
||||
}
|
||||
return res
|
||||
}
|
||||
}
|
||||
|
||||
@main
|
||||
enum _LinkedListStack {
|
||||
/* Driver Code */
|
||||
static func main() {
|
||||
/* スタックを初期化 */
|
||||
let stack = LinkedListStack()
|
||||
|
||||
/* 要素をプッシュ */
|
||||
stack.push(num: 1)
|
||||
stack.push(num: 3)
|
||||
stack.push(num: 2)
|
||||
stack.push(num: 5)
|
||||
stack.push(num: 4)
|
||||
print("スタック stack = \(stack.toArray())")
|
||||
|
||||
/* スタックトップの要素にアクセス */
|
||||
let peek = stack.peek()
|
||||
print("スタックトップ要素 peek = \(peek)")
|
||||
|
||||
/* 要素をポップ */
|
||||
let pop = stack.pop()
|
||||
print("ポップした要素 pop = \(pop)、ポップ後の stack = \(stack.toArray())")
|
||||
|
||||
/* スタックの長さを取得 */
|
||||
let size = stack.size()
|
||||
print("スタックの長さ size = \(size)")
|
||||
|
||||
/* 空かどうかを判定 */
|
||||
let isEmpty = stack.isEmpty()
|
||||
print("スタックが空かどうか = \(isEmpty)")
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
/**
|
||||
* File: queue.swift
|
||||
* Created Time: 2023-01-11
|
||||
* Author: nuomi1 (nuomi1@qq.com)
|
||||
*/
|
||||
|
||||
@main
|
||||
enum Queue {
|
||||
/* Driver Code */
|
||||
static func main() {
|
||||
/* キューを初期化 */
|
||||
// Swift には組み込みのキュークラスがないため、Array をキューとして使う
|
||||
var queue: [Int] = []
|
||||
|
||||
/* 要素をエンキュー */
|
||||
queue.append(1)
|
||||
queue.append(3)
|
||||
queue.append(2)
|
||||
queue.append(5)
|
||||
queue.append(4)
|
||||
print("キュー queue = \(queue)")
|
||||
|
||||
/* キュー先頭の要素にアクセス */
|
||||
let peek = queue.first!
|
||||
print("先頭要素 peek = \(peek)")
|
||||
|
||||
/* 要素をデキュー */
|
||||
// Array を用いる場合、pop の計算量は O(n)
|
||||
let pool = queue.removeFirst()
|
||||
print("デキューした要素 pop = \(pool)、デキュー後の queue = \(queue)")
|
||||
|
||||
/* キューの長さを取得 */
|
||||
let size = queue.count
|
||||
print("キューのサイズ size = \(size)")
|
||||
|
||||
/* キューが空かどうかを判定 */
|
||||
let isEmpty = queue.isEmpty
|
||||
print("キューが空かどうか = \(isEmpty)")
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
/**
|
||||
* File: stack.swift
|
||||
* Created Time: 2023-01-09
|
||||
* Author: nuomi1 (nuomi1@qq.com)
|
||||
*/
|
||||
|
||||
@main
|
||||
enum Stack {
|
||||
/* Driver Code */
|
||||
static func main() {
|
||||
/* スタックを初期化 */
|
||||
// Swift には組み込みのスタッククラスがないため、Array をスタックとして使う
|
||||
var stack: [Int] = []
|
||||
|
||||
/* 要素をプッシュ */
|
||||
stack.append(1)
|
||||
stack.append(3)
|
||||
stack.append(2)
|
||||
stack.append(5)
|
||||
stack.append(4)
|
||||
print("スタック stack = \(stack)")
|
||||
|
||||
/* スタックトップの要素にアクセス */
|
||||
let peek = stack.last!
|
||||
print("スタックトップ要素 peek = \(peek)")
|
||||
|
||||
/* 要素をポップ */
|
||||
let pop = stack.removeLast()
|
||||
print("ポップした要素 pop = \(pop)、ポップ後の stack = \(stack)")
|
||||
|
||||
/* スタックの長さを取得 */
|
||||
let size = stack.count
|
||||
print("スタックの長さ size = \(size)")
|
||||
|
||||
/* 空かどうかを判定 */
|
||||
let isEmpty = stack.isEmpty
|
||||
print("スタックが空かどうか = \(isEmpty)")
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,141 @@
|
||||
/**
|
||||
* File: array_binary_tree.swift
|
||||
* Created Time: 2023-07-23
|
||||
* Author: nuomi1 (nuomi1@qq.com)
|
||||
*/
|
||||
|
||||
import utils
|
||||
|
||||
/* 配列表現による二分木クラス */
|
||||
class ArrayBinaryTree {
|
||||
private var tree: [Int?]
|
||||
|
||||
/* コンストラクタ */
|
||||
init(arr: [Int?]) {
|
||||
tree = arr
|
||||
}
|
||||
|
||||
/* リスト容量 */
|
||||
func size() -> Int {
|
||||
tree.count
|
||||
}
|
||||
|
||||
/* インデックス i のノードの値を取得 */
|
||||
func val(i: Int) -> Int? {
|
||||
// インデックスが範囲外なら、空きを表す null を返す
|
||||
if i < 0 || i >= size() {
|
||||
return nil
|
||||
}
|
||||
return tree[i]
|
||||
}
|
||||
|
||||
/* インデックス i のノードの左子ノードのインデックスを取得 */
|
||||
func left(i: Int) -> Int {
|
||||
2 * i + 1
|
||||
}
|
||||
|
||||
/* インデックス i のノードの右子ノードのインデックスを取得 */
|
||||
func right(i: Int) -> Int {
|
||||
2 * i + 2
|
||||
}
|
||||
|
||||
/* インデックス i のノードの親ノードのインデックスを取得 */
|
||||
func parent(i: Int) -> Int {
|
||||
(i - 1) / 2
|
||||
}
|
||||
|
||||
/* レベル順走査 */
|
||||
func levelOrder() -> [Int] {
|
||||
var res: [Int] = []
|
||||
// 配列を直接走査する
|
||||
for i in 0 ..< size() {
|
||||
if let val = val(i: i) {
|
||||
res.append(val)
|
||||
}
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
/* 深さ優先探索 */
|
||||
private func dfs(i: Int, order: String, res: inout [Int]) {
|
||||
// 空きスロットなら返す
|
||||
guard let val = val(i: i) else {
|
||||
return
|
||||
}
|
||||
// 先行順走査
|
||||
if order == "pre" {
|
||||
res.append(val)
|
||||
}
|
||||
dfs(i: left(i: i), order: order, res: &res)
|
||||
// 中順走査
|
||||
if order == "in" {
|
||||
res.append(val)
|
||||
}
|
||||
dfs(i: right(i: i), order: order, res: &res)
|
||||
// 後順走査
|
||||
if order == "post" {
|
||||
res.append(val)
|
||||
}
|
||||
}
|
||||
|
||||
/* 先行順走査 */
|
||||
func preOrder() -> [Int] {
|
||||
var res: [Int] = []
|
||||
dfs(i: 0, order: "pre", res: &res)
|
||||
return res
|
||||
}
|
||||
|
||||
/* 中順走査 */
|
||||
func inOrder() -> [Int] {
|
||||
var res: [Int] = []
|
||||
dfs(i: 0, order: "in", res: &res)
|
||||
return res
|
||||
}
|
||||
|
||||
/* 後順走査 */
|
||||
func postOrder() -> [Int] {
|
||||
var res: [Int] = []
|
||||
dfs(i: 0, order: "post", res: &res)
|
||||
return res
|
||||
}
|
||||
}
|
||||
|
||||
@main
|
||||
enum _ArrayBinaryTree {
|
||||
/* Driver Code */
|
||||
static func main() {
|
||||
// 二分木を初期化
|
||||
// ここでは、配列から直接二分木を生成する関数を利用する
|
||||
let arr = [1, 2, 3, 4, nil, 6, 7, 8, 9, nil, nil, 12, nil, nil, 15]
|
||||
|
||||
let root = TreeNode.listToTree(arr: arr)
|
||||
print("\n二分木を初期化\n")
|
||||
print("二分木の配列表現:")
|
||||
print(arr)
|
||||
print("二分木の連結リスト表現:")
|
||||
PrintUtil.printTree(root: root)
|
||||
|
||||
// 配列表現による二分木クラス
|
||||
let abt = ArrayBinaryTree(arr: arr)
|
||||
|
||||
// ノードにアクセス
|
||||
let i = 1
|
||||
let l = abt.left(i: i)
|
||||
let r = abt.right(i: i)
|
||||
let p = abt.parent(i: i)
|
||||
print("\n現在のノードのインデックスは \(i) 、値は \(abt.val(i: i) as Any)")
|
||||
print("左の子ノードのインデックスは \(l) 、値は \(abt.val(i: l) as Any)")
|
||||
print("右の子ノードのインデックスは \(r) 、値は \(abt.val(i: r) as Any)")
|
||||
print("親ノードのインデックスは \(p) 、値は \(abt.val(i: p) as Any)")
|
||||
|
||||
// 木を走査
|
||||
var res = abt.levelOrder()
|
||||
print("\nレベル順走査:\(res)")
|
||||
res = abt.preOrder()
|
||||
print("先行順走査:\(res)")
|
||||
res = abt.inOrder()
|
||||
print("中間順走査:\(res)")
|
||||
res = abt.postOrder()
|
||||
print("後順走査は:\(res)")
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,230 @@
|
||||
/**
|
||||
* File: avl_tree.swift
|
||||
* Created Time: 2023-01-28
|
||||
* Author: nuomi1 (nuomi1@qq.com)
|
||||
*/
|
||||
|
||||
import utils
|
||||
|
||||
/* AVL 木 */
|
||||
class AVLTree {
|
||||
fileprivate var root: TreeNode? // 根ノード
|
||||
|
||||
init() {}
|
||||
|
||||
/* ノードの高さを取得 */
|
||||
func height(node: TreeNode?) -> Int {
|
||||
// 空ノードの高さは -1、葉ノードの高さは 0
|
||||
node?.height ?? -1
|
||||
}
|
||||
|
||||
/* ノードの高さを更新する */
|
||||
private func updateHeight(node: TreeNode?) {
|
||||
// ノードの高さは最も高い部分木の高さ + 1 に等しい
|
||||
node?.height = max(height(node: node?.left), height(node: node?.right)) + 1
|
||||
}
|
||||
|
||||
/* 平衡係数を取得 */
|
||||
func balanceFactor(node: TreeNode?) -> Int {
|
||||
// 空ノードの平衡係数は 0
|
||||
guard let node = node else { return 0 }
|
||||
// ノードの平衡係数 = 左部分木の高さ - 右部分木の高さ
|
||||
return height(node: node.left) - height(node: node.right)
|
||||
}
|
||||
|
||||
/* 右回転 */
|
||||
private func rightRotate(node: TreeNode?) -> TreeNode? {
|
||||
let child = node?.left
|
||||
let grandChild = child?.right
|
||||
// child を支点として node を右回転させる
|
||||
child?.right = node
|
||||
node?.left = grandChild
|
||||
// ノードの高さを更新する
|
||||
updateHeight(node: node)
|
||||
updateHeight(node: child)
|
||||
// 回転後の部分木の根ノードを返す
|
||||
return child
|
||||
}
|
||||
|
||||
/* 左回転 */
|
||||
private func leftRotate(node: TreeNode?) -> TreeNode? {
|
||||
let child = node?.right
|
||||
let grandChild = child?.left
|
||||
// child を支点として node を左回転させる
|
||||
child?.left = node
|
||||
node?.right = grandChild
|
||||
// ノードの高さを更新する
|
||||
updateHeight(node: node)
|
||||
updateHeight(node: child)
|
||||
// 回転後の部分木の根ノードを返す
|
||||
return child
|
||||
}
|
||||
|
||||
/* 回転操作を行い、この部分木の平衡を回復する */
|
||||
private func rotate(node: TreeNode?) -> TreeNode? {
|
||||
// ノード node の平衡係数を取得
|
||||
let balanceFactor = balanceFactor(node: node)
|
||||
// 左に偏った木
|
||||
if balanceFactor > 1 {
|
||||
if self.balanceFactor(node: node?.left) >= 0 {
|
||||
// 右回転
|
||||
return rightRotate(node: node)
|
||||
} else {
|
||||
// 左回転してから右回転
|
||||
node?.left = leftRotate(node: node?.left)
|
||||
return rightRotate(node: node)
|
||||
}
|
||||
}
|
||||
// 右に偏った木
|
||||
if balanceFactor < -1 {
|
||||
if self.balanceFactor(node: node?.right) <= 0 {
|
||||
// 左回転
|
||||
return leftRotate(node: node)
|
||||
} else {
|
||||
// 右回転してから左回転
|
||||
node?.right = rightRotate(node: node?.right)
|
||||
return leftRotate(node: node)
|
||||
}
|
||||
}
|
||||
// 平衡木なので回転不要、そのまま返す
|
||||
return node
|
||||
}
|
||||
|
||||
/* ノードを挿入 */
|
||||
func insert(val: Int) {
|
||||
root = insertHelper(node: root, val: val)
|
||||
}
|
||||
|
||||
/* ノードを再帰的に挿入する(補助メソッド) */
|
||||
private func insertHelper(node: TreeNode?, val: Int) -> TreeNode? {
|
||||
var node = node
|
||||
if node == nil {
|
||||
return TreeNode(x: val)
|
||||
}
|
||||
/* 1. 挿入位置を探索してノードを挿入 */
|
||||
if val < node!.val {
|
||||
node?.left = insertHelper(node: node?.left, val: val)
|
||||
} else if val > node!.val {
|
||||
node?.right = insertHelper(node: node?.right, val: val)
|
||||
} else {
|
||||
return node // 重複ノードは挿入せず、そのまま返す
|
||||
}
|
||||
updateHeight(node: node) // ノードの高さを更新する
|
||||
/* 2. 回転操作を行い、部分木の平衡を回復する */
|
||||
node = rotate(node: node)
|
||||
// 部分木の根ノードを返す
|
||||
return node
|
||||
}
|
||||
|
||||
/* ノードを削除 */
|
||||
func remove(val: Int) {
|
||||
root = removeHelper(node: root, val: val)
|
||||
}
|
||||
|
||||
/* ノードを再帰的に削除する(補助メソッド) */
|
||||
private func removeHelper(node: TreeNode?, val: Int) -> TreeNode? {
|
||||
var node = node
|
||||
if node == nil {
|
||||
return nil
|
||||
}
|
||||
/* 1. ノードを探索して削除 */
|
||||
if val < node!.val {
|
||||
node?.left = removeHelper(node: node?.left, val: val)
|
||||
} else if val > node!.val {
|
||||
node?.right = removeHelper(node: node?.right, val: val)
|
||||
} else {
|
||||
if node?.left == nil || node?.right == nil {
|
||||
let child = node?.left ?? node?.right
|
||||
// 子ノード数 = 0 の場合、node をそのまま削除して返す
|
||||
if child == nil {
|
||||
return nil
|
||||
}
|
||||
// 子ノード数 = 1 の場合、node をそのまま削除する
|
||||
else {
|
||||
node = child
|
||||
}
|
||||
} else {
|
||||
// 子ノード数 = 2 の場合、中順走査の次のノードを削除し、そのノードで現在のノードを置き換える
|
||||
var temp = node?.right
|
||||
while temp?.left != nil {
|
||||
temp = temp?.left
|
||||
}
|
||||
node?.right = removeHelper(node: node?.right, val: temp!.val)
|
||||
node?.val = temp!.val
|
||||
}
|
||||
}
|
||||
updateHeight(node: node) // ノードの高さを更新する
|
||||
/* 2. 回転操作を行い、部分木の平衡を回復する */
|
||||
node = rotate(node: node)
|
||||
// 部分木の根ノードを返す
|
||||
return node
|
||||
}
|
||||
|
||||
/* ノードを探索 */
|
||||
func search(val: Int) -> TreeNode? {
|
||||
var cur = root
|
||||
while cur != nil {
|
||||
// 目標ノードは cur の右部分木にある
|
||||
if cur!.val < val {
|
||||
cur = cur?.right
|
||||
}
|
||||
// 目標ノードは cur の左部分木にある
|
||||
else if cur!.val > val {
|
||||
cur = cur?.left
|
||||
}
|
||||
// 目標ノードが見つかったらループを抜ける
|
||||
else {
|
||||
break
|
||||
}
|
||||
}
|
||||
// 目標ノードを返す
|
||||
return cur
|
||||
}
|
||||
}
|
||||
|
||||
@main
|
||||
enum _AVLTree {
|
||||
static func testInsert(tree: AVLTree, val: Int) {
|
||||
tree.insert(val: val)
|
||||
print("\nノード \(val) を挿入後、AVL 木は")
|
||||
PrintUtil.printTree(root: tree.root)
|
||||
}
|
||||
|
||||
static func testRemove(tree: AVLTree, val: Int) {
|
||||
tree.remove(val: val)
|
||||
print("\nノード \(val) を削除後、AVL 木は")
|
||||
PrintUtil.printTree(root: tree.root)
|
||||
}
|
||||
|
||||
/* Driver Code */
|
||||
static func main() {
|
||||
/* 空の AVL 木を初期化する */
|
||||
let avlTree = AVLTree()
|
||||
|
||||
/* ノードを挿入 */
|
||||
// ノード挿入後に AVL 木がどのように平衡を保つかに注目してほしい
|
||||
testInsert(tree: avlTree, val: 1)
|
||||
testInsert(tree: avlTree, val: 2)
|
||||
testInsert(tree: avlTree, val: 3)
|
||||
testInsert(tree: avlTree, val: 4)
|
||||
testInsert(tree: avlTree, val: 5)
|
||||
testInsert(tree: avlTree, val: 8)
|
||||
testInsert(tree: avlTree, val: 7)
|
||||
testInsert(tree: avlTree, val: 9)
|
||||
testInsert(tree: avlTree, val: 10)
|
||||
testInsert(tree: avlTree, val: 6)
|
||||
|
||||
/* 重複ノードを挿入する */
|
||||
testInsert(tree: avlTree, val: 7)
|
||||
|
||||
/* ノードを削除 */
|
||||
// ノード削除後に AVL 木がどのように平衡を保つかに注目してほしい
|
||||
testRemove(tree: avlTree, val: 8) // 次数 0 のノードを削除する
|
||||
testRemove(tree: avlTree, val: 5) // 次数 1 のノードを削除する
|
||||
testRemove(tree: avlTree, val: 4) // 次数 2 のノードを削除する
|
||||
|
||||
/* ノードを検索 */
|
||||
let node = avlTree.search(val: 7)
|
||||
print("\n見つかったノードオブジェクトは \(node!)、ノード値 = \(node!.val)")
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,173 @@
|
||||
/**
|
||||
* File: binary_search_tree.swift
|
||||
* Created Time: 2023-01-26
|
||||
* Author: nuomi1 (nuomi1@qq.com)
|
||||
*/
|
||||
|
||||
import utils
|
||||
|
||||
/* 二分探索木 */
|
||||
class BinarySearchTree {
|
||||
private var root: TreeNode?
|
||||
|
||||
/* コンストラクタ */
|
||||
init() {
|
||||
// 空の木を初期化する
|
||||
root = nil
|
||||
}
|
||||
|
||||
/* 二分木の根ノードを取得 */
|
||||
func getRoot() -> TreeNode? {
|
||||
root
|
||||
}
|
||||
|
||||
/* ノードを探索 */
|
||||
func search(num: Int) -> TreeNode? {
|
||||
var cur = root
|
||||
// ループで探索し、葉ノードを越えたら抜ける
|
||||
while cur != nil {
|
||||
// 目標ノードは cur の右部分木にある
|
||||
if cur!.val < num {
|
||||
cur = cur?.right
|
||||
}
|
||||
// 目標ノードは cur の左部分木にある
|
||||
else if cur!.val > num {
|
||||
cur = cur?.left
|
||||
}
|
||||
// 目標ノードが見つかったらループを抜ける
|
||||
else {
|
||||
break
|
||||
}
|
||||
}
|
||||
// 目標ノードを返す
|
||||
return cur
|
||||
}
|
||||
|
||||
/* ノードを挿入 */
|
||||
func insert(num: Int) {
|
||||
// 木が空なら、根ノードを初期化する
|
||||
if root == nil {
|
||||
root = TreeNode(x: num)
|
||||
return
|
||||
}
|
||||
var cur = root
|
||||
var pre: TreeNode?
|
||||
// ループで探索し、葉ノードを越えたら抜ける
|
||||
while cur != nil {
|
||||
// 重複ノードが見つかったら、直ちに返す
|
||||
if cur!.val == num {
|
||||
return
|
||||
}
|
||||
pre = cur
|
||||
// 挿入位置は cur の右部分木にある
|
||||
if cur!.val < num {
|
||||
cur = cur?.right
|
||||
}
|
||||
// 挿入位置は cur の左部分木にある
|
||||
else {
|
||||
cur = cur?.left
|
||||
}
|
||||
}
|
||||
// ノードを挿入
|
||||
let node = TreeNode(x: num)
|
||||
if pre!.val < num {
|
||||
pre?.right = node
|
||||
} else {
|
||||
pre?.left = node
|
||||
}
|
||||
}
|
||||
|
||||
/* ノードを削除 */
|
||||
func remove(num: Int) {
|
||||
// 木が空なら、そのまま早期リターンする
|
||||
if root == nil {
|
||||
return
|
||||
}
|
||||
var cur = root
|
||||
var pre: TreeNode?
|
||||
// ループで探索し、葉ノードを越えたら抜ける
|
||||
while cur != nil {
|
||||
// 削除対象のノードが見つかったら、ループを抜ける
|
||||
if cur!.val == num {
|
||||
break
|
||||
}
|
||||
pre = cur
|
||||
// 削除対象ノードは cur の右部分木にある
|
||||
if cur!.val < num {
|
||||
cur = cur?.right
|
||||
}
|
||||
// 削除対象ノードは cur の左部分木にある
|
||||
else {
|
||||
cur = cur?.left
|
||||
}
|
||||
}
|
||||
// 削除対象ノードがなければそのまま返す
|
||||
if cur == nil {
|
||||
return
|
||||
}
|
||||
// 子ノード数 = 0 or 1
|
||||
if cur?.left == nil || cur?.right == nil {
|
||||
// 子ノード数が 0 / 1 のとき、child = null / その子ノード
|
||||
let child = cur?.left ?? cur?.right
|
||||
// ノード cur を削除する
|
||||
if cur !== root {
|
||||
if pre?.left === cur {
|
||||
pre?.left = child
|
||||
} else {
|
||||
pre?.right = child
|
||||
}
|
||||
} else {
|
||||
// 削除ノードが根ノードなら、根ノードを再設定
|
||||
root = child
|
||||
}
|
||||
}
|
||||
// 子ノード数 = 2
|
||||
else {
|
||||
// 中順走査における cur の次ノードを取得
|
||||
var tmp = cur?.right
|
||||
while tmp?.left != nil {
|
||||
tmp = tmp?.left
|
||||
}
|
||||
// ノード tmp を再帰的に削除
|
||||
remove(num: tmp!.val)
|
||||
// tmp で cur を上書きする
|
||||
cur?.val = tmp!.val
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@main
|
||||
enum _BinarySearchTree {
|
||||
/* Driver Code */
|
||||
static func main() {
|
||||
/* 二分探索木を初期化 */
|
||||
let bst = BinarySearchTree()
|
||||
// 注意:挿入順序が異なると異なる二分木が生成される。このシーケンスからは完全二分木を生成できる
|
||||
let nums = [8, 4, 12, 2, 6, 10, 14, 1, 3, 5, 7, 9, 11, 13, 15]
|
||||
for num in nums {
|
||||
bst.insert(num: num)
|
||||
}
|
||||
print("\n初期化された二分木は\n")
|
||||
PrintUtil.printTree(root: bst.getRoot())
|
||||
|
||||
/* ノードを探索 */
|
||||
let node = bst.search(num: 7)
|
||||
print("\n見つかったノードオブジェクトは \(node!)、ノード値 = \(node!.val)")
|
||||
|
||||
/* ノードを挿入 */
|
||||
bst.insert(num: 16)
|
||||
print("\nノード 16 を挿入後、二分木は\n")
|
||||
PrintUtil.printTree(root: bst.getRoot())
|
||||
|
||||
/* ノードを削除 */
|
||||
bst.remove(num: 1)
|
||||
print("\nノード 1 を削除後、二分木は\n")
|
||||
PrintUtil.printTree(root: bst.getRoot())
|
||||
bst.remove(num: 2)
|
||||
print("\nノード 2 を削除後、二分木は\n")
|
||||
PrintUtil.printTree(root: bst.getRoot())
|
||||
bst.remove(num: 4)
|
||||
print("\nノード 4 を削除後、二分木は\n")
|
||||
PrintUtil.printTree(root: bst.getRoot())
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
/**
|
||||
* File: binary_tree.swift
|
||||
* Created Time: 2023-01-18
|
||||
* Author: nuomi1 (nuomi1@qq.com)
|
||||
*/
|
||||
|
||||
import utils
|
||||
|
||||
@main
|
||||
enum BinaryTree {
|
||||
/* Driver Code */
|
||||
static func main() {
|
||||
/* 二分木を初期化 */
|
||||
// ノードを初期化
|
||||
let n1 = TreeNode(x: 1)
|
||||
let n2 = TreeNode(x: 2)
|
||||
let n3 = TreeNode(x: 3)
|
||||
let n4 = TreeNode(x: 4)
|
||||
let n5 = TreeNode(x: 5)
|
||||
// ノード間の参照(ポインタ)を構築する
|
||||
n1.left = n2
|
||||
n1.right = n3
|
||||
n2.left = n4
|
||||
n2.right = n5
|
||||
print("\n二分木を初期化\n")
|
||||
PrintUtil.printTree(root: n1)
|
||||
|
||||
/* ノードの挿入と削除 */
|
||||
let P = TreeNode(x: 0)
|
||||
// n1 -> n2 の間にノード P を挿入
|
||||
n1.left = P
|
||||
P.left = n2
|
||||
print("\nノード P を挿入後\n")
|
||||
PrintUtil.printTree(root: n1)
|
||||
// ノード P を削除
|
||||
n1.left = n2
|
||||
print("\nノード P を削除後\n")
|
||||
PrintUtil.printTree(root: n1)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
/**
|
||||
* File: binary_tree_bfs.swift
|
||||
* Created Time: 2023-01-18
|
||||
* Author: nuomi1 (nuomi1@qq.com)
|
||||
*/
|
||||
|
||||
import utils
|
||||
|
||||
/* レベル順走査 */
|
||||
func levelOrder(root: TreeNode) -> [Int] {
|
||||
// キューを初期化し、ルートノードを追加する
|
||||
var queue: [TreeNode] = [root]
|
||||
// 走査順序を保存するためのリストを初期化する
|
||||
var list: [Int] = []
|
||||
while !queue.isEmpty {
|
||||
let node = queue.removeFirst() // デキュー
|
||||
list.append(node.val) // ノードの値を保存する
|
||||
if let left = node.left {
|
||||
queue.append(left) // 左子ノードをキューに追加
|
||||
}
|
||||
if let right = node.right {
|
||||
queue.append(right) // 右子ノードをキューに追加
|
||||
}
|
||||
}
|
||||
return list
|
||||
}
|
||||
|
||||
@main
|
||||
enum BinaryTreeBFS {
|
||||
/* Driver Code */
|
||||
static func main() {
|
||||
/* 二分木を初期化 */
|
||||
// ここでは、配列から直接二分木を生成する関数を利用する
|
||||
let node = TreeNode.listToTree(arr: [1, 2, 3, 4, 5, 6, 7])!
|
||||
print("\n二分木を初期化\n")
|
||||
PrintUtil.printTree(root: node)
|
||||
|
||||
/* レベル順走査 */
|
||||
let list = levelOrder(root: node)
|
||||
print("\nレベル順走査のノード出力シーケンス = \(list)")
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
/**
|
||||
* File: binary_tree_dfs.swift
|
||||
* Created Time: 2023-01-18
|
||||
* Author: nuomi1 (nuomi1@qq.com)
|
||||
*/
|
||||
|
||||
import utils
|
||||
|
||||
// 走査順序を格納するリストを初期化
|
||||
var list: [Int] = []
|
||||
|
||||
/* 先行順走査 */
|
||||
func preOrder(root: TreeNode?) {
|
||||
guard let root = root else {
|
||||
return
|
||||
}
|
||||
// 訪問順序:根ノード -> 左部分木 -> 右部分木
|
||||
list.append(root.val)
|
||||
preOrder(root: root.left)
|
||||
preOrder(root: root.right)
|
||||
}
|
||||
|
||||
/* 中順走査 */
|
||||
func inOrder(root: TreeNode?) {
|
||||
guard let root = root else {
|
||||
return
|
||||
}
|
||||
// 訪問優先順: 左部分木 -> 根ノード -> 右部分木
|
||||
inOrder(root: root.left)
|
||||
list.append(root.val)
|
||||
inOrder(root: root.right)
|
||||
}
|
||||
|
||||
/* 後順走査 */
|
||||
func postOrder(root: TreeNode?) {
|
||||
guard let root = root else {
|
||||
return
|
||||
}
|
||||
// 訪問優先順: 左部分木 -> 右部分木 -> 根ノード
|
||||
postOrder(root: root.left)
|
||||
postOrder(root: root.right)
|
||||
list.append(root.val)
|
||||
}
|
||||
|
||||
@main
|
||||
enum BinaryTreeDFS {
|
||||
/* Driver Code */
|
||||
static func main() {
|
||||
/* 二分木を初期化 */
|
||||
// ここでは、配列から直接二分木を生成する関数を利用する
|
||||
let root = TreeNode.listToTree(arr: [1, 2, 3, 4, 5, 6, 7])!
|
||||
print("\n二分木を初期化\n")
|
||||
PrintUtil.printTree(root: root)
|
||||
|
||||
/* 先行順走査 */
|
||||
list.removeAll()
|
||||
preOrder(root: root)
|
||||
print("\n前順走査のノード出力シーケンス = \(list)")
|
||||
|
||||
/* 中順走査 */
|
||||
list.removeAll()
|
||||
inOrder(root: root)
|
||||
print("\n中順走査のノード出力シーケンス = \(list)")
|
||||
|
||||
/* 後順走査 */
|
||||
list.removeAll()
|
||||
postOrder(root: root)
|
||||
print("\n後順走査のノード出力シーケンス = \(list)")
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
/**
|
||||
* File: ListNode.swift
|
||||
* Created Time: 2023-01-02
|
||||
* Author: nuomi1 (nuomi1@qq.com)
|
||||
*/
|
||||
|
||||
public class ListNode: Hashable {
|
||||
public var val: Int // ノード値
|
||||
public var next: ListNode? // 後続ノードへの参照
|
||||
|
||||
public init(x: Int) {
|
||||
val = x
|
||||
}
|
||||
|
||||
public static func == (lhs: ListNode, rhs: ListNode) -> Bool {
|
||||
lhs.val == rhs.val && lhs.next.map { ObjectIdentifier($0) } == rhs.next.map { ObjectIdentifier($0) }
|
||||
}
|
||||
|
||||
public func hash(into hasher: inout Hasher) {
|
||||
hasher.combine(val)
|
||||
hasher.combine(next.map { ObjectIdentifier($0) })
|
||||
}
|
||||
|
||||
public static func arrToLinkedList(arr: [Int]) -> ListNode? {
|
||||
let dum = ListNode(x: 0)
|
||||
var head: ListNode? = dum
|
||||
for val in arr {
|
||||
head?.next = ListNode(x: val)
|
||||
head = head?.next
|
||||
}
|
||||
return dum.next
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
/**
|
||||
* File: Pair.swift
|
||||
* Created Time: 2023-06-28
|
||||
* Author: nuomi1 (nuomi1@qq.com)
|
||||
*/
|
||||
|
||||
/* キーと値の組 */
|
||||
public class Pair: Equatable {
|
||||
public var key: Int
|
||||
public var val: String
|
||||
|
||||
public init(key: Int, val: String) {
|
||||
self.key = key
|
||||
self.val = val
|
||||
}
|
||||
|
||||
public static func == (lhs: Pair, rhs: Pair) -> Bool {
|
||||
lhs.key == rhs.key && lhs.val == rhs.val
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,93 @@
|
||||
/**
|
||||
* File: PrintUtil.swift
|
||||
* Created Time: 2023-01-02
|
||||
* Author: nuomi1 (nuomi1@qq.com)
|
||||
*/
|
||||
|
||||
public enum PrintUtil {
|
||||
private class Trunk {
|
||||
var prev: Trunk?
|
||||
var str: String
|
||||
|
||||
init(prev: Trunk?, str: String) {
|
||||
self.prev = prev
|
||||
self.str = str
|
||||
}
|
||||
}
|
||||
|
||||
public static func printLinkedList(head: ListNode) {
|
||||
var head: ListNode? = head
|
||||
var list: [String] = []
|
||||
while head != nil {
|
||||
list.append("\(head!.val)")
|
||||
head = head?.next
|
||||
}
|
||||
print(list.joined(separator: " -> "))
|
||||
}
|
||||
|
||||
public static func printTree(root: TreeNode?) {
|
||||
printTree(root: root, prev: nil, isRight: false)
|
||||
}
|
||||
|
||||
private static func printTree(root: TreeNode?, prev: Trunk?, isRight: Bool) {
|
||||
if root == nil {
|
||||
return
|
||||
}
|
||||
|
||||
var prevStr = " "
|
||||
let trunk = Trunk(prev: prev, str: prevStr)
|
||||
|
||||
printTree(root: root?.right, prev: trunk, isRight: true)
|
||||
|
||||
if prev == nil {
|
||||
trunk.str = "———"
|
||||
} else if isRight {
|
||||
trunk.str = "/———"
|
||||
prevStr = " |"
|
||||
} else {
|
||||
trunk.str = "\\———"
|
||||
prev?.str = prevStr
|
||||
}
|
||||
|
||||
showTrunks(p: trunk)
|
||||
print(" \(root!.val)")
|
||||
|
||||
if prev != nil {
|
||||
prev?.str = prevStr
|
||||
}
|
||||
trunk.str = " |"
|
||||
|
||||
printTree(root: root?.left, prev: trunk, isRight: false)
|
||||
}
|
||||
|
||||
private static func showTrunks(p: Trunk?) {
|
||||
if p == nil {
|
||||
return
|
||||
}
|
||||
|
||||
showTrunks(p: p?.prev)
|
||||
print(p!.str, terminator: "")
|
||||
}
|
||||
|
||||
public static func printHashMap<K, V>(map: [K: V]) {
|
||||
for (key, value) in map {
|
||||
print("\(key) -> \(value)")
|
||||
}
|
||||
}
|
||||
|
||||
public static func printHeap(queue: [Int]) {
|
||||
print("ヒープの配列表現:", terminator: "")
|
||||
print(queue)
|
||||
print("ヒープの木構造表現:")
|
||||
let root = TreeNode.listToTree(arr: queue)
|
||||
printTree(root: root)
|
||||
}
|
||||
|
||||
public static func printMatrix<T>(matrix: [[T]]) {
|
||||
print("[")
|
||||
for row in matrix {
|
||||
print(" \(row),")
|
||||
}
|
||||
print("]")
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
/**
|
||||
* File: TreeNode.swift
|
||||
* Created Time: 2023-01-02
|
||||
* Author: nuomi1 (nuomi1@qq.com)
|
||||
*/
|
||||
|
||||
/* 二分木ノードクラス */
|
||||
public class TreeNode {
|
||||
public var val: Int // ノード値
|
||||
public var height: Int // ノードの高さ
|
||||
public var left: TreeNode? // 左子ノードへの参照
|
||||
public var right: TreeNode? // 右子ノードへの参照
|
||||
|
||||
/* コンストラクタ */
|
||||
public init(x: Int) {
|
||||
val = x
|
||||
height = 0
|
||||
}
|
||||
|
||||
// シリアライズの符号化規則は次を参照してください:
|
||||
// https://www.hello-algo.com/chapter_tree/array_representation_of_tree/
|
||||
// 二分木の配列表現:
|
||||
// [1, 2, 3, 4, nil, 6, 7, 8, 9, nil, nil, 12, nil, nil, 15]
|
||||
// 二分木の連結リスト表現:
|
||||
// /——— 15
|
||||
// /——— 7
|
||||
// /——— 3
|
||||
// | \\——— 6
|
||||
// | \\——— 12
|
||||
// ——— 1
|
||||
// \\——— 2
|
||||
// | /——— 9
|
||||
// \\——— 4
|
||||
// \\——— 8
|
||||
|
||||
/* リストを二分木にデシリアライズする: 再帰 */
|
||||
private static func listToTreeDFS(arr: [Int?], i: Int) -> TreeNode? {
|
||||
if i < 0 || i >= arr.count || arr[i] == nil {
|
||||
return nil
|
||||
}
|
||||
let root = TreeNode(x: arr[i]!)
|
||||
root.left = listToTreeDFS(arr: arr, i: 2 * i + 1)
|
||||
root.right = listToTreeDFS(arr: arr, i: 2 * i + 2)
|
||||
return root
|
||||
}
|
||||
|
||||
/* リストを二分木にデシリアライズする */
|
||||
public static func listToTree(arr: [Int?]) -> TreeNode? {
|
||||
listToTreeDFS(arr: arr, i: 0)
|
||||
}
|
||||
|
||||
/* 二分木をリストにシリアライズする: 再帰 */
|
||||
private static func treeToListDFS(root: TreeNode?, i: Int, res: inout [Int?]) {
|
||||
if root == nil {
|
||||
return
|
||||
}
|
||||
while i >= res.count {
|
||||
res.append(nil)
|
||||
}
|
||||
res[i] = root?.val
|
||||
treeToListDFS(root: root?.left, i: 2 * i + 1, res: &res)
|
||||
treeToListDFS(root: root?.right, i: 2 * i + 2, res: &res)
|
||||
}
|
||||
|
||||
/* 二分木をリストにシリアライズする */
|
||||
public static func treeToList(root: TreeNode?) -> [Int?] {
|
||||
var res: [Int?] = []
|
||||
treeToListDFS(root: root, i: 0, res: &res)
|
||||
return res
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
/**
|
||||
* File: Vertex.swift
|
||||
* Created Time: 2023-02-19
|
||||
* Author: nuomi1 (nuomi1@qq.com)
|
||||
*/
|
||||
|
||||
/* 頂点クラス */
|
||||
public class Vertex: Hashable {
|
||||
public var val: Int
|
||||
|
||||
public init(val: Int) {
|
||||
self.val = val
|
||||
}
|
||||
|
||||
public static func == (lhs: Vertex, rhs: Vertex) -> Bool {
|
||||
lhs.val == rhs.val
|
||||
}
|
||||
|
||||
public func hash(into hasher: inout Hasher) {
|
||||
hasher.combine(val)
|
||||
}
|
||||
|
||||
/* 値リスト vals を入力し、頂点リスト vets を返す */
|
||||
public static func valsToVets(vals: [Int]) -> [Vertex] {
|
||||
vals.map { Vertex(val: $0) }
|
||||
}
|
||||
|
||||
/* 頂点リスト vets を入力し、値リスト vals を返す */
|
||||
public static func vetsToVals(vets: [Vertex]) -> [Int] {
|
||||
vets.map { $0.val }
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user