mirror of
https://github.com/krahets/hello-algo.git
synced 2026-09-01 20:57:13 +00:00
Add ru version (#1865)
* Add Russian docs site baseline * Add Russian localized codebase * Polish Russian code wording * Update ru code translation. * Update code translation and chapter covers. * Fix pythontutor extraction. * Add README and landing page. * placeholder of profiles * Use figures of English version * Remove chapter paperbook
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)
|
||||
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
|
||||
}
|
||||
|
||||
/* Вставить элемент num по индексу index в массив */
|
||||
func insert(nums: inout [Int], num: Int, index: Int) {
|
||||
// Сдвинуть элемент с индексом index и все последующие элементы на одну позицию назад
|
||||
for i in nums.indices.dropFirst(index).reversed() {
|
||||
nums[i] = nums[i - 1]
|
||||
}
|
||||
// Присвоить num элементу по индексу index
|
||||
nums[index] = num
|
||||
}
|
||||
|
||||
/* Удалить элемент по индексу index */
|
||||
func remove(nums: inout [Int], index: Int) {
|
||||
// Сдвинуть все элементы после индекса index на одну позицию вперед
|
||||
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("После вставки числа 6 по индексу 3 nums = \(nums)")
|
||||
|
||||
/* Удаление элемента */
|
||||
remove(nums: &nums, index: 2)
|
||||
print("После удаления элемента по индексу 2 nums = \(nums)")
|
||||
|
||||
/* Обход массива */
|
||||
traverse(nums: nums)
|
||||
|
||||
/* Поиск элемента */
|
||||
let index = find(nums: nums, target: 3)
|
||||
print("Поиск элемента 3 в nums: индекс = \(index)")
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,90 @@
|
||||
/**
|
||||
* File: linked_list.swift
|
||||
* Created Time: 2023-01-08
|
||||
* Author: nuomi1 (nuomi1@qq.com)
|
||||
*/
|
||||
|
||||
import utils
|
||||
|
||||
/* Вставить узел P после узла n0 в связном списке */
|
||||
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("После вставки числа 6 по индексу 3 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
|
||||
}
|
||||
|
||||
/* Объединить два списка */
|
||||
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 и все следующие элементы на одну позицию назад
|
||||
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 на одну позицию вперед
|
||||
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("После вставки числа 6 по индексу 3 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 (двойное обновление) */
|
||||
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("\nРезультат суммирования в цикле for res = \(res)")
|
||||
|
||||
res = whileLoop(n: n)
|
||||
print("\nРезультат суммирования в цикле while res = \(res)")
|
||||
|
||||
res = whileLoopII(n: n)
|
||||
print("\nРезультат суммирования в цикле while (двойное обновление) 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
|
||||
// На каждом шаге клетка делится надвое, образуя последовательность 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
|
||||
// Из одного получается 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
|
||||
}
|
||||
|
||||
/* Найти индекс числа 1 в массиве nums */
|
||||
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)
|
||||
*/
|
||||
|
||||
/* Переместить один диск */
|
||||
func move(src: inout [Int], tar: inout [Int]) {
|
||||
// Снять диск с вершины src
|
||||
let pan = src.popLast()!
|
||||
// Положить диск на вершину tar
|
||||
tar.append(pan)
|
||||
}
|
||||
|
||||
/* Решить задачу Ханойской башни f(i) */
|
||||
func dfs(i: Int, src: inout [Int], buf: inout [Int], tar: inout [Int]) {
|
||||
// Если в src остался только один диск, сразу переместить его в tar
|
||||
if i == 1 {
|
||||
move(src: &src, tar: &tar)
|
||||
return
|
||||
}
|
||||
// Подзадача f(i-1): переместить верхние i-1 дисков из src в buf с помощью tar
|
||||
dfs(i: i - 1, src: &src, buf: &tar, tar: &buf)
|
||||
// Подзадача f(1): переместить оставшийся один диск из src в tar
|
||||
move(src: &src, tar: &tar)
|
||||
// Подзадача f(i-1): переместить верхние i-1 дисков из buf в tar с помощью src
|
||||
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
|
||||
// Хвост списка соответствует вершине столбца
|
||||
// Переместить верхние n дисков из src в C с помощью B
|
||||
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
|
||||
}
|
||||
// Если два символа равны, сразу пропустить их
|
||||
if s.utf8CString[i - 1] == t.utf8CString[j - 1] {
|
||||
return editDistanceDFS(s: s, t: t, i: i - 1, j: j - 1)
|
||||
}
|
||||
// Минимальное число шагов редактирования = минимальное число шагов для вставки, удаления и замены + 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]
|
||||
}
|
||||
// Если два символа равны, сразу пропустить их
|
||||
if s.utf8CString[i - 1] == t.utf8CString[j - 1] {
|
||||
return editDistanceDFS(s: s, t: t, i: i - 1, j: j - 1)
|
||||
}
|
||||
// Минимальное число шагов редактирования = минимальное число шагов для вставки, удаления и замены + 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] {
|
||||
// Если два символа равны, сразу пропустить их
|
||||
dp[i][j] = dp[i - 1][j - 1]
|
||||
} else {
|
||||
// Минимальное число шагов редактирования = минимальное число шагов для вставки, удаления и замены + 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] {
|
||||
// Если два символа равны, сразу пропустить их
|
||||
dp[j] = leftup
|
||||
} else {
|
||||
// Минимальное число шагов редактирования = минимальное число шагов для вставки, удаления и замены + 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]
|
||||
// Вернуть вариант с большей стоимостью из двух возможных
|
||||
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]
|
||||
// Сохранить и вернуть вариант с большей стоимостью из двух решений
|
||||
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)
|
||||
// Добавить строку в матрицу смежности
|
||||
let newRow = Array(repeating: 0, count: n)
|
||||
adjMat.append(newRow)
|
||||
// Добавить столбец в матрицу смежности
|
||||
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 {
|
||||
// Создать список предметов с двумя свойствами: вес и стоимость
|
||||
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
|
||||
// Выполнять жадный выбор в цикле, пока две доски не встретятся
|
||||
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 — число троек, а 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После добавления хеш-таблица имеет вид\nКлюч -> Значение")
|
||||
map.print()
|
||||
|
||||
/* Операция поиска */
|
||||
// Ввести в хеш-таблицу ключ key и получить значение value
|
||||
let name = map.get(key: 15937)!
|
||||
print("\nДля номера 15937 найдено имя \(name)")
|
||||
|
||||
/* Операция удаления */
|
||||
// Удалить пару (key, value) из хеш-таблицы
|
||||
map.remove(key: 10583)
|
||||
print("\nПосле удаления 10583 хеш-таблица имеет вид\nКлюч -> Значение")
|
||||
map.print()
|
||||
|
||||
/* Обход хеш-таблицы */
|
||||
print("\nОтдельный обход пар ключ-значение")
|
||||
for pair in map.pairSet() {
|
||||
print("\(pair.key) -> \(pair.val)")
|
||||
}
|
||||
print("\nОтдельный обход ключей")
|
||||
for key in map.keySet() {
|
||||
print(key)
|
||||
}
|
||||
print("\nОтдельный обход значений")
|
||||
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 Algo"
|
||||
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После добавления хеш-таблица имеет вид\nКлюч -> Значение")
|
||||
PrintUtil.printHashMap(map: map)
|
||||
|
||||
/* Операция поиска */
|
||||
// Ввести в хеш-таблицу ключ key и получить значение value
|
||||
let name = map[15937]!
|
||||
print("\nДля номера 15937 найдено имя \(name)")
|
||||
|
||||
/* Операция удаления */
|
||||
// Удалить пару (key, value) из хеш-таблицы
|
||||
map.removeValue(forKey: 10583)
|
||||
print("\nПосле удаления 10583 хеш-таблица имеет вид\nКлюч -> Значение")
|
||||
PrintUtil.printHashMap(map: map)
|
||||
|
||||
/* Обход хеш-таблицы */
|
||||
print("\nОтдельный обход пар ключ-значение")
|
||||
for (key, value) in map {
|
||||
print("\(key) -> \(value)")
|
||||
}
|
||||
print("\nОтдельный обход ключей")
|
||||
for key in map.keys {
|
||||
print(key)
|
||||
}
|
||||
print("\nОтдельный обход значений")
|
||||
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После добавления хеш-таблица имеет вид\nКлюч -> Значение")
|
||||
map.print()
|
||||
|
||||
/* Операция поиска */
|
||||
// Ввести в хеш-таблицу ключ key и получить значение value
|
||||
let name = map.get(key: 13276)
|
||||
print("\nДля номера 13276 найдено имя \(name!)")
|
||||
|
||||
/* Операция удаления */
|
||||
// Удалить пару (key, value) из хеш-таблицы
|
||||
map.remove(key: 12836)
|
||||
print("\nПосле удаления 12836 хеш-таблица имеет вид\nКлюч -> Значение")
|
||||
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После добавления хеш-таблица имеет вид\nКлюч -> Значение")
|
||||
map.print()
|
||||
|
||||
/* Операция поиска */
|
||||
// Ввести в хеш-таблицу ключ key и получить значение value
|
||||
let name = map.get(key: 13276)
|
||||
print("\nДля номера 13276 найдено имя \(name!)")
|
||||
|
||||
/* Операция удаления */
|
||||
// Удалить пару (key, value) из хеш-таблицы
|
||||
map.remove(key: 16750)
|
||||
print("\nПосле удаления 16750 хеш-таблица имеет вид\nКлюч -> Значение")
|
||||
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 Algo"
|
||||
|
||||
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() {
|
||||
/* Инициализация кучи */
|
||||
// Тип Heap в Swift одновременно поддерживает максимальную и минимальную кучу
|
||||
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
|
||||
// Выполнить heapify для всех узлов, кроме листовых
|
||||
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)
|
||||
// Завершить heapify, когда «корневой узел уже пройден» или «узел не требует исправления»
|
||||
if p < 0 || maxHeap[i] <= maxHeap[p] {
|
||||
break
|
||||
}
|
||||
// Поменять два узла местами
|
||||
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
|
||||
}
|
||||
// Поменять два узла местами
|
||||
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 указывают на первый элемент массива и позицию сразу за последним элементом соответственно
|
||||
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] {
|
||||
// Два вложенных цикла, временная сложность 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
|
||||
// ====== Основной код ======
|
||||
// Метод 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
|
||||
}
|
||||
// Перезаписать исходный массив nums массивом результата res
|
||||
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
|
||||
}
|
||||
// Поменять два узла местами
|
||||
nums.swapAt(i, ma)
|
||||
// Циклическое просеивание вниз
|
||||
i = ma
|
||||
}
|
||||
}
|
||||
|
||||
/* Сортировка кучей */
|
||||
func heapSort(nums: inout [Int]) {
|
||||
// Построение кучи: выполнить heapify для всех узлов, кроме листовых
|
||||
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] на одну позицию вправо
|
||||
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) // Поменять эти два элемента местами
|
||||
}
|
||||
nums.swapAt(i, left) // Переместить опорный элемент на границу двух подмассивов
|
||||
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)
|
||||
}
|
||||
|
||||
/* Класс быстрой сортировки (оптимизация медианным опорным элементом) */
|
||||
/* Выбрать медиану из трех кандидатов */
|
||||
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
|
||||
}
|
||||
|
||||
/* Разбиение с опорными указателями (медиана трех) */
|
||||
func partitionMedian(nums: inout [Int], left: Int, right: Int) -> Int {
|
||||
// Выбрать медиану из трех кандидатов
|
||||
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)
|
||||
// Выполнить быструю сортировку для более короткого из двух подмассивов
|
||||
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)
|
||||
*/
|
||||
|
||||
/* Получить k-й разряд элемента num, где exp = 10^(k-1) */
|
||||
func digit(num: Int, exp: Int) -> Int {
|
||||
// Передача exp вместо k позволяет избежать повторного дорогостоящего вычисления степени
|
||||
(num / exp) % 10
|
||||
}
|
||||
|
||||
/* Сортировка подсчетом (сортировка по k-му разряду nums) */
|
||||
func countingSortDigit(nums: inout [Int], exp: Int) {
|
||||
// Разряды десятичной системы лежат в диапазоне 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) // Получить k-й разряд nums[i], обозначив его как 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 // Получить индекс j цифры d в массиве
|
||||
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 // Указатель head, указывающий на первый элемент очереди
|
||||
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
|
||||
}
|
||||
// Указатель головы сдвигается на одну позицию влево
|
||||
// С помощью операции взятия по модулю 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()
|
||||
// Указатель головы сдвигается на одну позицию назад
|
||||
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 // Указатель head, указывающий на первый элемент очереди
|
||||
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()
|
||||
// Указатель head сдвигается на одну позицию назад; если он выходит за конец, то возвращается в начало массива
|
||||
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)-го раунда операций enqueue и dequeue 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)
|
||||
// Если очередь пуста, сделать так, чтобы и head, и tail указывали на этот узел
|
||||
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
|
||||
// Выполнить правое вращение узла node вокруг child
|
||||
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
|
||||
// Выполнить левое вращение узла node вокруг child
|
||||
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 или 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)
|
||||
// Перезаписать cur значением tmp
|
||||
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)
|
||||
// Вставить узел P между n1 -> n2
|
||||
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