mirror of
https://github.com/krahets/hello-algo.git
synced 2026-08-17 22:10:59 +00:00
Re-translate the Japanese version (#1871)
* Retranslate Japanese docs with GPT-5.4 * Retranslate Japanese code with GPT-5.4
This commit is contained in:
@@ -0,0 +1,102 @@
|
||||
/**
|
||||
* File: array.kt
|
||||
* Created Time: 2024-01-25
|
||||
* Author: curtishd (1023632660@qq.com)
|
||||
*/
|
||||
|
||||
package chapter_array_and_linkedlist
|
||||
|
||||
import java.util.concurrent.ThreadLocalRandom
|
||||
|
||||
/* 要素へランダムアクセス */
|
||||
fun randomAccess(nums: IntArray): Int {
|
||||
// 区間 [0, nums.size) からランダムに数字を 1 つ選ぶ
|
||||
val randomIndex = ThreadLocalRandom.current().nextInt(0, nums.size)
|
||||
// ランダムな要素を取得して返す
|
||||
val randomNum = nums[randomIndex]
|
||||
return randomNum
|
||||
}
|
||||
|
||||
/* 配列長を拡張する */
|
||||
fun extend(nums: IntArray, enlarge: Int): IntArray {
|
||||
// 拡張後の長さを持つ配列を初期化する
|
||||
val res = IntArray(nums.size + enlarge)
|
||||
// 元の配列の全要素を新しい配列にコピー
|
||||
for (i in nums.indices) {
|
||||
res[i] = nums[i]
|
||||
}
|
||||
// 拡張後の新しい配列を返す
|
||||
return res
|
||||
}
|
||||
|
||||
/* 配列の index 番目に要素 num を挿入 */
|
||||
fun insert(nums: IntArray, num: Int, index: Int) {
|
||||
// インデックス index 以降の全要素を 1 つ後ろへ移動する
|
||||
for (i in nums.size - 1 downTo index + 1) {
|
||||
nums[i] = nums[i - 1]
|
||||
}
|
||||
// index の要素に num を代入する
|
||||
nums[index] = num
|
||||
}
|
||||
|
||||
/* index の要素を削除する */
|
||||
fun remove(nums: IntArray, index: Int) {
|
||||
// インデックス index より後ろの全要素を 1 つ前へ移動する
|
||||
for (i in index..<nums.size - 1) {
|
||||
nums[i] = nums[i + 1]
|
||||
}
|
||||
}
|
||||
|
||||
/* 配列を走査 */
|
||||
fun traverse(nums: IntArray) {
|
||||
var count = 0
|
||||
// インデックスで配列を走査
|
||||
for (i in nums.indices) {
|
||||
count += nums[i]
|
||||
}
|
||||
// 配列要素を直接走査
|
||||
for (j in nums) {
|
||||
count += j
|
||||
}
|
||||
}
|
||||
|
||||
/* 配列内で指定要素を探す */
|
||||
fun find(nums: IntArray, target: Int): Int {
|
||||
for (i in nums.indices) {
|
||||
if (nums[i] == target)
|
||||
return i
|
||||
}
|
||||
return -1
|
||||
}
|
||||
|
||||
/* Driver Code */
|
||||
fun main() {
|
||||
/* 配列を初期化 */
|
||||
val arr = IntArray(5)
|
||||
println("配列 arr = ${arr.contentToString()}")
|
||||
var nums = intArrayOf(1, 3, 2, 5, 4)
|
||||
println("配列 nums = ${nums.contentToString()}")
|
||||
|
||||
/* ランダムアクセス */
|
||||
val randomNum: Int = randomAccess(nums)
|
||||
println("nums からランダムな要素 $randomNum を取得")
|
||||
|
||||
/* 長さを拡張 */
|
||||
nums = extend(nums, 3)
|
||||
println("配列の長さを 8 まで拡張し,nums = ${nums.contentToString()}")
|
||||
|
||||
/* 要素を挿入する */
|
||||
insert(nums, 6, 3)
|
||||
println("インデックス 3 に数値 6 を挿入し,nums = ${nums.contentToString()}")
|
||||
|
||||
/* 要素を削除 */
|
||||
remove(nums, 2)
|
||||
println("インデックス 2 の要素を削除し,nums = ${nums.contentToString()}")
|
||||
|
||||
/* 配列を走査 */
|
||||
traverse(nums)
|
||||
|
||||
/* 要素を探索する */
|
||||
val index: Int = find(nums, 3)
|
||||
println("nums 内で要素 3 を検索し,インデックス = $index")
|
||||
}
|
||||
@@ -0,0 +1,88 @@
|
||||
/**
|
||||
* File: linked_list.kt
|
||||
* Created Time: 2024-01-25
|
||||
* Author: curtishd (1023632660@qq.com)
|
||||
*/
|
||||
|
||||
package chapter_array_and_linkedlist
|
||||
|
||||
import utils.ListNode
|
||||
import utils.printLinkedList
|
||||
|
||||
/* 連結リストでノード n0 の後ろにノード P を挿入する */
|
||||
fun insert(n0: ListNode?, p: ListNode?) {
|
||||
val n1 = n0?.next
|
||||
p?.next = n1
|
||||
n0?.next = p
|
||||
}
|
||||
|
||||
/* 連結リストでノード n0 の直後のノードを削除する */
|
||||
fun remove(n0: ListNode?) {
|
||||
if (n0?.next == null)
|
||||
return
|
||||
// n0 -> P -> n1
|
||||
val p = n0.next
|
||||
val n1 = p?.next
|
||||
n0.next = n1
|
||||
}
|
||||
|
||||
/* 連結リスト内で index 番目のノードにアクセス */
|
||||
fun access(head: ListNode?, index: Int): ListNode? {
|
||||
var h = head
|
||||
for (i in 0..<index) {
|
||||
if (h == null)
|
||||
return null
|
||||
h = h.next
|
||||
}
|
||||
return h
|
||||
}
|
||||
|
||||
/* 連結リストで値が target の最初のノードを探す */
|
||||
fun find(head: ListNode?, target: Int): Int {
|
||||
var index = 0
|
||||
var h = head
|
||||
while (h != null) {
|
||||
if (h._val == target)
|
||||
return index
|
||||
h = h.next
|
||||
index++
|
||||
}
|
||||
return -1
|
||||
}
|
||||
|
||||
/* Driver Code */
|
||||
fun main() {
|
||||
/* 連結リストを初期化 */
|
||||
// 各ノードを初期化
|
||||
val n0 = ListNode(1)
|
||||
val n1 = ListNode(3)
|
||||
val n2 = ListNode(2)
|
||||
val n3 = ListNode(5)
|
||||
val n4 = ListNode(4)
|
||||
|
||||
// ノード間の参照を構築する
|
||||
n0.next = n1
|
||||
n1.next = n2
|
||||
n2.next = n3
|
||||
n3.next = n4
|
||||
println("初期化した連結リストは")
|
||||
printLinkedList(n0)
|
||||
|
||||
/* ノードを挿入 */
|
||||
insert(n0, ListNode(0))
|
||||
println("ノード挿入後の連結リストは")
|
||||
printLinkedList(n0)
|
||||
|
||||
/* ノードを削除 */
|
||||
remove(n0)
|
||||
println("ノード削除後の連結リストは")
|
||||
printLinkedList(n0)
|
||||
|
||||
/* ノードにアクセス */
|
||||
val node = access(n0, 3)!!
|
||||
println("連結リストのインデックス 3 にあるノードの値 = ${node._val}")
|
||||
|
||||
/* ノードを探索 */
|
||||
val index = find(n0, 2)
|
||||
println("連結リスト内で値が 2 のノードのインデックス = $index")
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
/**
|
||||
* File: list.kt
|
||||
* Created Time: 2024-01-25
|
||||
* Author: curtishd (1023632660@qq.com)
|
||||
*/
|
||||
|
||||
package chapter_array_and_linkedlist
|
||||
|
||||
/* Driver Code */
|
||||
fun main() {
|
||||
/* リストを初期化 */
|
||||
// 可変集合
|
||||
val nums = mutableListOf(1, 3, 2, 5, 4)
|
||||
println("リスト nums = $nums")
|
||||
|
||||
/* 要素にアクセス */
|
||||
val num = nums[1]
|
||||
println("インデックス 1 の要素にアクセスし,num = $num")
|
||||
|
||||
/* 要素を更新 */
|
||||
nums[1] = 0
|
||||
println("インデックス 1 の要素を 0 に更新し,nums = $nums")
|
||||
|
||||
/* リストを空にする */
|
||||
nums.clear()
|
||||
println("リストを空にした後の nums = $nums")
|
||||
|
||||
/* 末尾に要素を追加 */
|
||||
nums.add(1)
|
||||
nums.add(3)
|
||||
nums.add(2)
|
||||
nums.add(5)
|
||||
nums.add(4)
|
||||
println("要素追加後の nums = $nums")
|
||||
|
||||
/* 中間に要素を挿入 */
|
||||
nums.add(3, 6)
|
||||
println("インデックス 3 に数値 6 を挿入し,nums = $nums")
|
||||
|
||||
/* 要素を削除 */
|
||||
nums.removeAt(3)
|
||||
println("インデックス 3 の要素を削除し,nums = $nums")
|
||||
|
||||
/* インデックスでリストを走査 */
|
||||
var count = 0
|
||||
for (i in nums.indices) {
|
||||
count += nums[i]
|
||||
}
|
||||
|
||||
/* リスト要素を直接走査 */
|
||||
for (j in nums) {
|
||||
count += j
|
||||
}
|
||||
|
||||
/* 2 つのリストを連結する */
|
||||
val nums1 = mutableListOf(6, 8, 7, 10, 9)
|
||||
nums.addAll(nums1)
|
||||
println("リスト nums1 を nums の後ろに連結し,nums = $nums")
|
||||
|
||||
/* リストをソート */
|
||||
nums.sort()
|
||||
println("リストをソートした後の nums = $nums")
|
||||
}
|
||||
@@ -0,0 +1,139 @@
|
||||
/**
|
||||
* File: my_list.kt
|
||||
* Created Time: 2024-01-25
|
||||
* Author: curtishd (1023632660@qq.com)
|
||||
*/
|
||||
|
||||
package chapter_array_and_linkedlist
|
||||
|
||||
/* リストクラス */
|
||||
class MyList {
|
||||
private var arr: IntArray = intArrayOf() // 配列(リスト要素を格納)
|
||||
private var capacity: Int = 10 // リスト容量
|
||||
private var size: Int = 0 // リストの長さ(現在の要素数)
|
||||
private var extendRatio: Int = 2 // リスト拡張時の増加倍率
|
||||
|
||||
/* コンストラクタ */
|
||||
init {
|
||||
arr = IntArray(capacity)
|
||||
}
|
||||
|
||||
/* リストの長さを取得(現在の要素数) */
|
||||
fun size(): Int {
|
||||
return size
|
||||
}
|
||||
|
||||
/* リスト容量を取得する */
|
||||
fun capacity(): Int {
|
||||
return capacity
|
||||
}
|
||||
|
||||
/* 要素にアクセス */
|
||||
fun get(index: Int): Int {
|
||||
// インデックスが範囲外なら例外を送出する。以下同様
|
||||
if (index < 0 || index >= size)
|
||||
throw IndexOutOfBoundsException("インデックスが範囲外")
|
||||
return arr[index]
|
||||
}
|
||||
|
||||
/* 要素を更新 */
|
||||
fun set(index: Int, num: Int) {
|
||||
if (index < 0 || index >= size)
|
||||
throw IndexOutOfBoundsException("インデックスが範囲外")
|
||||
arr[index] = num
|
||||
}
|
||||
|
||||
/* 末尾に要素を追加 */
|
||||
fun add(num: Int) {
|
||||
// 要素数が容量を超えると、拡張機構が発動する
|
||||
if (size == capacity())
|
||||
extendCapacity()
|
||||
arr[size] = num
|
||||
// 要素数を更新
|
||||
size++
|
||||
}
|
||||
|
||||
/* 中間に要素を挿入 */
|
||||
fun insert(index: Int, num: Int) {
|
||||
if (index < 0 || index >= size)
|
||||
throw IndexOutOfBoundsException("インデックスが範囲外")
|
||||
// 要素数が容量を超えると、拡張機構が発動する
|
||||
if (size == capacity())
|
||||
extendCapacity()
|
||||
// index 以降の要素をすべて 1 つ後ろへずらす
|
||||
for (j in size - 1 downTo index)
|
||||
arr[j + 1] = arr[j]
|
||||
arr[index] = num
|
||||
// 要素数を更新
|
||||
size++
|
||||
}
|
||||
|
||||
/* 要素を削除 */
|
||||
fun remove(index: Int): Int {
|
||||
if (index < 0 || index >= size)
|
||||
throw IndexOutOfBoundsException("インデックスが範囲外")
|
||||
val num = arr[index]
|
||||
// インデックス index より後の要素をすべて 1 つ前に移動する
|
||||
for (j in index..<size - 1)
|
||||
arr[j] = arr[j + 1]
|
||||
// 要素数を更新
|
||||
size--
|
||||
// 削除された要素を返す
|
||||
return num
|
||||
}
|
||||
|
||||
/* リストの拡張 */
|
||||
fun extendCapacity() {
|
||||
// 元の配列の extendRatio 倍の長さを持つ新しい配列を作成し、元の配列をコピーする
|
||||
arr = arr.copyOf(capacity() * extendRatio)
|
||||
// リストの容量を更新
|
||||
capacity = arr.size
|
||||
}
|
||||
|
||||
/* リストを配列に変換する */
|
||||
fun toArray(): IntArray {
|
||||
val size = size()
|
||||
// 有効長の範囲内のリスト要素のみを変換
|
||||
val arr = IntArray(size)
|
||||
for (i in 0..<size) {
|
||||
arr[i] = get(i)
|
||||
}
|
||||
return arr
|
||||
}
|
||||
}
|
||||
|
||||
/* Driver Code */
|
||||
fun main() {
|
||||
/* リストを初期化 */
|
||||
val nums = MyList()
|
||||
/* 末尾に要素を追加 */
|
||||
nums.add(1)
|
||||
nums.add(3)
|
||||
nums.add(2)
|
||||
nums.add(5)
|
||||
nums.add(4)
|
||||
println("リスト nums = ${nums.toArray().contentToString()},容量 = ${nums.capacity()},長さ = ${nums.size()}")
|
||||
|
||||
/* 中間に要素を挿入 */
|
||||
nums.insert(3, 6)
|
||||
println("インデックス 3 に数値 6 を挿入し,nums = ${nums.toArray().contentToString()}")
|
||||
|
||||
/* 要素を削除 */
|
||||
nums.remove(3)
|
||||
println("インデックス 3 の要素を削除し,nums = ${nums.toArray().contentToString()}")
|
||||
|
||||
/* 要素にアクセス */
|
||||
val num = nums.get(1)
|
||||
println("インデックス 1 の要素にアクセスし,num = $num")
|
||||
|
||||
/* 要素を更新 */
|
||||
nums.set(1, 0)
|
||||
println("インデックス 1 の要素を 0 に更新し,nums = ${nums.toArray().contentToString()}")
|
||||
|
||||
/* 拡張機構をテストする */
|
||||
for (i in 0..9) {
|
||||
// i = 5 のとき、リスト長が容量を超えるため、この時点で拡張機構が発動する
|
||||
nums.add(i)
|
||||
}
|
||||
println("拡張後のリスト nums = ${nums.toArray().contentToString()},容量 = ${nums.capacity()},長さ = ${nums.size()}")
|
||||
}
|
||||
@@ -0,0 +1,85 @@
|
||||
/**
|
||||
* File: n_queens.kt
|
||||
* Created Time: 2024-01-25
|
||||
* Author: curtishd (1023632660@qq.com)
|
||||
*/
|
||||
|
||||
package chapter_backtracking.n_queens
|
||||
|
||||
/* バックトラッキング:N クイーン */
|
||||
fun backtrack(
|
||||
row: Int,
|
||||
n: Int,
|
||||
state: MutableList<MutableList<String>>,
|
||||
res: MutableList<MutableList<MutableList<String>>?>,
|
||||
cols: BooleanArray,
|
||||
diags1: BooleanArray,
|
||||
diags2: BooleanArray
|
||||
) {
|
||||
// すべての行への配置が完了したら、解を記録する
|
||||
if (row == n) {
|
||||
val copyState = mutableListOf<MutableList<String>>()
|
||||
for (sRow in state) {
|
||||
copyState.add(sRow.toMutableList())
|
||||
}
|
||||
res.add(copyState)
|
||||
return
|
||||
}
|
||||
// すべての列を走査
|
||||
for (col in 0..<n) {
|
||||
// このマスに対応する主対角線と副対角線を計算
|
||||
val diag1 = row - col + n - 1
|
||||
val diag2 = row + col
|
||||
// 枝刈り:そのマスの列、主対角線、副対角線にクイーンがあってはならない
|
||||
if (!cols[col] && !diags1[diag1] && !diags2[diag2]) {
|
||||
// 試行:そのマスにクイーンを置く
|
||||
state[row][col] = "Q"
|
||||
diags2[diag2] = true
|
||||
diags1[diag1] = diags2[diag2]
|
||||
cols[col] = diags1[diag1]
|
||||
// 次の行に配置する
|
||||
backtrack(row + 1, n, state, res, cols, diags1, diags2)
|
||||
// 戻す:そのマスを空きマスに戻す
|
||||
state[row][col] = "#"
|
||||
diags2[diag2] = false
|
||||
diags1[diag1] = diags2[diag2]
|
||||
cols[col] = diags1[diag1]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* N クイーンを解く */
|
||||
fun nQueens(n: Int): MutableList<MutableList<MutableList<String>>?> {
|
||||
// n*n の盤面を初期化する。'Q' はクイーン、'#' は空きマスを表す
|
||||
val state = mutableListOf<MutableList<String>>()
|
||||
for (i in 0..<n) {
|
||||
val row = mutableListOf<String>()
|
||||
for (j in 0..<n) {
|
||||
row.add("#")
|
||||
}
|
||||
state.add(row)
|
||||
}
|
||||
val cols = BooleanArray(n) // 列にクイーンがあるか記録
|
||||
val diags1 = BooleanArray(2 * n - 1) // 主対角線にクイーンがあるかを記録
|
||||
val diags2 = BooleanArray(2 * n - 1) // 副対角線にクイーンがあるかを記録
|
||||
val res = mutableListOf<MutableList<MutableList<String>>?>()
|
||||
|
||||
backtrack(0, n, state, res, cols, diags1, diags2)
|
||||
|
||||
return res
|
||||
}
|
||||
|
||||
/* Driver Code */
|
||||
fun main() {
|
||||
val n = 4
|
||||
val res = nQueens(n)
|
||||
|
||||
println("入力された盤面の縦横は $n")
|
||||
println("クイーンの配置パターンは全部で ${res.size} 通り")
|
||||
for (state in res) {
|
||||
println("--------------------")
|
||||
for (row in state!!) {
|
||||
println(row)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
/**
|
||||
* File: permutations_i.kt
|
||||
* Created Time: 2024-01-25
|
||||
* Author: curtishd (1023632660@qq.com)
|
||||
*/
|
||||
|
||||
package chapter_backtracking.permutations_i
|
||||
|
||||
/* バックトラッキング:順列 I */
|
||||
fun backtrack(
|
||||
state: MutableList<Int>,
|
||||
choices: IntArray,
|
||||
selected: BooleanArray,
|
||||
res: MutableList<MutableList<Int>?>
|
||||
) {
|
||||
// 状態の長さが要素数に等しければ、解を記録
|
||||
if (state.size == choices.size) {
|
||||
res.add(state.toMutableList())
|
||||
return
|
||||
}
|
||||
// すべての選択肢を走査
|
||||
for (i in choices.indices) {
|
||||
val choice = choices[i]
|
||||
// 枝刈り:要素の重複選択を許可しない
|
||||
if (!selected[i]) {
|
||||
// 試行: 選択を行い、状態を更新
|
||||
selected[i] = true
|
||||
state.add(choice)
|
||||
// 次の選択へ進む
|
||||
backtrack(state, choices, selected, res)
|
||||
// バックトラック:選択を取り消し、前の状態に戻す
|
||||
selected[i] = false
|
||||
state.removeAt(state.size - 1)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* 全順列 I */
|
||||
fun permutationsI(nums: IntArray): MutableList<MutableList<Int>?> {
|
||||
val res = mutableListOf<MutableList<Int>?>()
|
||||
backtrack(mutableListOf(), nums, BooleanArray(nums.size), res)
|
||||
return res
|
||||
}
|
||||
|
||||
/* Driver Code */
|
||||
fun main() {
|
||||
val nums = intArrayOf(1, 2, 3)
|
||||
|
||||
val res = permutationsI(nums)
|
||||
|
||||
println("入力配列 nums = ${nums.contentToString()}")
|
||||
println("すべての順列 res = $res")
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
/**
|
||||
* File: permutations_ii.kt
|
||||
* Created Time: 2024-01-25
|
||||
* Author: curtishd (1023632660@qq.com)
|
||||
*/
|
||||
|
||||
package chapter_backtracking.permutations_ii
|
||||
|
||||
/* バックトラッキング:順列 II */
|
||||
fun backtrack(
|
||||
state: MutableList<Int>,
|
||||
choices: IntArray,
|
||||
selected: BooleanArray,
|
||||
res: MutableList<MutableList<Int>?>
|
||||
) {
|
||||
// 状態の長さが要素数に等しければ、解を記録
|
||||
if (state.size == choices.size) {
|
||||
res.add(state.toMutableList())
|
||||
return
|
||||
}
|
||||
// すべての選択肢を走査
|
||||
val duplicated = HashSet<Int>()
|
||||
for (i in choices.indices) {
|
||||
val choice = choices[i]
|
||||
// 枝刈り:要素の重複選択を許可せず、同値要素の重複選択も許可しない
|
||||
if (!selected[i] && !duplicated.contains(choice)) {
|
||||
// 試行: 選択を行い、状態を更新
|
||||
duplicated.add(choice) // 選択済みの要素値を記録
|
||||
selected[i] = true
|
||||
state.add(choice)
|
||||
// 次の選択へ進む
|
||||
backtrack(state, choices, selected, res)
|
||||
// バックトラック:選択を取り消し、前の状態に戻す
|
||||
selected[i] = false
|
||||
state.removeAt(state.size - 1)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* 全順列 II */
|
||||
fun permutationsII(nums: IntArray): MutableList<MutableList<Int>?> {
|
||||
val res = mutableListOf<MutableList<Int>?>()
|
||||
backtrack(mutableListOf(), nums, BooleanArray(nums.size), res)
|
||||
return res
|
||||
}
|
||||
|
||||
/* Driver Code */
|
||||
fun main() {
|
||||
val nums = intArrayOf(1, 2, 2)
|
||||
val res = permutationsII(nums)
|
||||
|
||||
println("入力配列 nums = ${nums.contentToString()}")
|
||||
println("すべての順列 res = $res")
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
/**
|
||||
* File: preorder_traversal_i_compact.kt
|
||||
* Created Time: 2024-01-25
|
||||
* Author: curtishd (1023632660@qq.com)
|
||||
*/
|
||||
|
||||
package chapter_backtracking.preorder_traversal_i_compact
|
||||
|
||||
import utils.TreeNode
|
||||
import utils.printTree
|
||||
|
||||
var res: MutableList<TreeNode>? = null
|
||||
|
||||
/* 前順走査:例題 1 */
|
||||
fun preOrder(root: TreeNode?) {
|
||||
if (root == null) {
|
||||
return
|
||||
}
|
||||
if (root._val == 7) {
|
||||
// 解を記録
|
||||
res!!.add(root)
|
||||
}
|
||||
preOrder(root.left)
|
||||
preOrder(root.right)
|
||||
}
|
||||
|
||||
/* Driver Code */
|
||||
fun main() {
|
||||
val root = TreeNode.listToTree(mutableListOf(1, 7, 3, 4, 5, 6, 7))
|
||||
println("\n二分木を初期化")
|
||||
printTree(root)
|
||||
|
||||
// 先行順走査
|
||||
res = mutableListOf()
|
||||
preOrder(root)
|
||||
|
||||
println("\n値が 7 のノードをすべて出力")
|
||||
val vals = mutableListOf<Int>()
|
||||
for (node in res!!) {
|
||||
vals.add(node._val)
|
||||
}
|
||||
println(vals)
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
/**
|
||||
* File: preorder_traversal_ii_compact.kt
|
||||
* Created Time: 2024-01-25
|
||||
* Author: curtishd (1023632660@qq.com)
|
||||
*/
|
||||
|
||||
package chapter_backtracking.preorder_traversal_ii_compact
|
||||
|
||||
import utils.TreeNode
|
||||
import utils.printTree
|
||||
|
||||
var path: MutableList<TreeNode>? = null
|
||||
var res: MutableList<MutableList<TreeNode>>? = null
|
||||
|
||||
/* 前順走査:例題 2 */
|
||||
fun preOrder(root: TreeNode?) {
|
||||
if (root == null) {
|
||||
return
|
||||
}
|
||||
// 試す
|
||||
path!!.add(root)
|
||||
if (root._val == 7) {
|
||||
// 解を記録
|
||||
res!!.add(path!!.toMutableList())
|
||||
}
|
||||
preOrder(root.left)
|
||||
preOrder(root.right)
|
||||
// バックトラック
|
||||
path!!.removeAt(path!!.size - 1)
|
||||
}
|
||||
|
||||
/* Driver Code */
|
||||
fun main() {
|
||||
val root = TreeNode.listToTree(mutableListOf(1, 7, 3, 4, 5, 6, 7))
|
||||
println("\n二分木を初期化")
|
||||
printTree(root)
|
||||
|
||||
// 先行順走査
|
||||
path = mutableListOf()
|
||||
res = mutableListOf()
|
||||
preOrder(root)
|
||||
|
||||
println("\n根ノードからノード 7 までのすべての経路を出力")
|
||||
for (path in res!!) {
|
||||
val _vals = mutableListOf<Int>()
|
||||
for (node in path) {
|
||||
_vals.add(node._val)
|
||||
}
|
||||
println(_vals)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
/**
|
||||
* File: preorder_traversal_iii_compact.kt
|
||||
* Created Time: 2024-01-25
|
||||
* Author: curtishd (1023632660@qq.com)
|
||||
*/
|
||||
|
||||
package chapter_backtracking.preorder_traversal_iii_compact
|
||||
|
||||
import utils.TreeNode
|
||||
import utils.printTree
|
||||
|
||||
var path: MutableList<TreeNode>? = null
|
||||
var res: MutableList<MutableList<TreeNode>>? = null
|
||||
|
||||
/* 前順走査:例題 3 */
|
||||
fun preOrder(root: TreeNode?) {
|
||||
// 枝刈り
|
||||
if (root == null || root._val == 3) {
|
||||
return
|
||||
}
|
||||
// 試す
|
||||
path!!.add(root)
|
||||
if (root._val == 7) {
|
||||
// 解を記録
|
||||
res!!.add(path!!.toMutableList())
|
||||
}
|
||||
preOrder(root.left)
|
||||
preOrder(root.right)
|
||||
// バックトラック
|
||||
path!!.removeAt(path!!.size - 1)
|
||||
}
|
||||
|
||||
/* Driver Code */
|
||||
fun main() {
|
||||
val root = TreeNode.listToTree(mutableListOf(1, 7, 3, 4, 5, 6, 7))
|
||||
println("\n二分木を初期化")
|
||||
printTree(root)
|
||||
|
||||
// 先行順走査
|
||||
path = mutableListOf()
|
||||
res = mutableListOf()
|
||||
preOrder(root)
|
||||
|
||||
println("\n根ノードからノード 7 までのすべての経路を出力し,経路には値が 3 のノードを含めない")
|
||||
for (path in res!!) {
|
||||
val _vals = mutableListOf<Int>()
|
||||
for (node in path) {
|
||||
_vals.add(node._val)
|
||||
}
|
||||
println(_vals)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
/**
|
||||
* File: preorder_traversal_iii_template.kt
|
||||
* Created Time: 2024-01-25
|
||||
* Author: curtishd (1023632660@qq.com)
|
||||
*/
|
||||
|
||||
package chapter_backtracking.preorder_traversal_iii_template
|
||||
|
||||
import utils.TreeNode
|
||||
import utils.printTree
|
||||
|
||||
/* 現在の状態が解かどうかを判定 */
|
||||
fun isSolution(state: MutableList<TreeNode?>): Boolean {
|
||||
return state.isNotEmpty() && state[state.size - 1]?._val == 7
|
||||
}
|
||||
|
||||
/* 解を記録 */
|
||||
fun recordSolution(state: MutableList<TreeNode?>?, res: MutableList<MutableList<TreeNode?>?>) {
|
||||
res.add(state!!.toMutableList())
|
||||
}
|
||||
|
||||
/* 現在の状態で、この選択が有効かどうかを判定 */
|
||||
fun isValid(state: MutableList<TreeNode?>?, choice: TreeNode?): Boolean {
|
||||
return choice != null && choice._val != 3
|
||||
}
|
||||
|
||||
/* 状態を更新 */
|
||||
fun makeChoice(state: MutableList<TreeNode?>, choice: TreeNode?) {
|
||||
state.add(choice)
|
||||
}
|
||||
|
||||
/* 状態を元に戻す */
|
||||
fun undoChoice(state: MutableList<TreeNode?>, choice: TreeNode?) {
|
||||
state.removeLast()
|
||||
}
|
||||
|
||||
/* バックトラッキング:例題 3 */
|
||||
fun backtrack(
|
||||
state: MutableList<TreeNode?>,
|
||||
choices: MutableList<TreeNode?>,
|
||||
res: MutableList<MutableList<TreeNode?>?>
|
||||
) {
|
||||
// 解かどうかを確認
|
||||
if (isSolution(state)) {
|
||||
// 解を記録
|
||||
recordSolution(state, res)
|
||||
}
|
||||
// すべての選択肢を走査
|
||||
for (choice in choices) {
|
||||
// 枝刈り:選択が妥当かを確認する
|
||||
if (isValid(state, choice)) {
|
||||
// 試行: 選択を行い、状態を更新
|
||||
makeChoice(state, choice)
|
||||
// 次の選択へ進む
|
||||
backtrack(state, mutableListOf(choice!!.left, choice.right), res)
|
||||
// バックトラック:選択を取り消し、前の状態に戻す
|
||||
undoChoice(state, choice)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Driver Code */
|
||||
fun main() {
|
||||
val root = TreeNode.listToTree(mutableListOf(1, 7, 3, 4, 5, 6, 7))
|
||||
println("\n二分木を初期化")
|
||||
printTree(root)
|
||||
|
||||
// バックトラッキング法
|
||||
val res = mutableListOf<MutableList<TreeNode?>?>()
|
||||
backtrack(mutableListOf(), mutableListOf(root), res)
|
||||
|
||||
println("\n根ノードからノード 7 までのすべての経路を出力し,経路に値が 3 のノードを含まないことを条件とする")
|
||||
for (path in res) {
|
||||
val vals = mutableListOf<Int>()
|
||||
for (node in path!!) {
|
||||
if (node != null) {
|
||||
vals.add(node._val)
|
||||
}
|
||||
}
|
||||
println(vals)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
/**
|
||||
* File: subset_sum_i.kt
|
||||
* Created Time: 2024-01-25
|
||||
* Author: curtishd (1023632660@qq.com)
|
||||
*/
|
||||
|
||||
package chapter_backtracking.subset_sum_i
|
||||
|
||||
/* バックトラッキング:部分和 I */
|
||||
fun backtrack(
|
||||
state: MutableList<Int>,
|
||||
target: Int,
|
||||
choices: IntArray,
|
||||
start: Int,
|
||||
res: MutableList<MutableList<Int>?>
|
||||
) {
|
||||
// 部分集合の和が target に等しければ、解を記録
|
||||
if (target == 0) {
|
||||
res.add(state.toMutableList())
|
||||
return
|
||||
}
|
||||
// すべての選択肢を走査
|
||||
// 枝刈り 2: start から走査し、重複する部分集合の生成を避ける
|
||||
for (i in start..<choices.size) {
|
||||
// 枝刈り1:部分集合の和が target を超えたら、直ちにループを終了する
|
||||
// 配列はソート済みで後続要素のほうが大きく、部分集合の和は必ず target を超えるため
|
||||
if (target - choices[i] < 0) {
|
||||
break
|
||||
}
|
||||
// 試す:選択を行い、target と start を更新
|
||||
state.add(choices[i])
|
||||
// 次の選択へ進む
|
||||
backtrack(state, target - choices[i], choices, i, res)
|
||||
// バックトラック:選択を取り消し、前の状態に戻す
|
||||
state.removeAt(state.size - 1)
|
||||
}
|
||||
}
|
||||
|
||||
/* 部分和 I を解く */
|
||||
fun subsetSumI(nums: IntArray, target: Int): MutableList<MutableList<Int>?> {
|
||||
val state = mutableListOf<Int>() // 状態(部分集合)
|
||||
nums.sort() // nums をソート
|
||||
val start = 0 // 開始点を走査
|
||||
val res = mutableListOf<MutableList<Int>?>() // 結果リスト(部分集合のリスト)
|
||||
backtrack(state, target, nums, start, res)
|
||||
return res
|
||||
}
|
||||
|
||||
/* Driver Code */
|
||||
fun main() {
|
||||
val nums = intArrayOf(3, 4, 5)
|
||||
val target = 9
|
||||
|
||||
val res = subsetSumI(nums, target)
|
||||
|
||||
println("入力配列 nums = ${nums.contentToString()}, target = $target")
|
||||
println("和が $target に等しいすべての部分集合 res = $res")
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
/**
|
||||
* File: subset_sum_i_native.kt
|
||||
* Created Time: 2024-01-25
|
||||
* Author: curtishd (1023632660@qq.com)
|
||||
*/
|
||||
|
||||
package chapter_backtracking.subset_sum_i_naive
|
||||
|
||||
/* バックトラッキング:部分和 I */
|
||||
fun backtrack(
|
||||
state: MutableList<Int>,
|
||||
target: Int,
|
||||
total: Int,
|
||||
choices: IntArray,
|
||||
res: MutableList<MutableList<Int>?>
|
||||
) {
|
||||
// 部分集合の和が target に等しければ、解を記録
|
||||
if (total == target) {
|
||||
res.add(state.toMutableList())
|
||||
return
|
||||
}
|
||||
// すべての選択肢を走査
|
||||
for (i in choices.indices) {
|
||||
// 枝刈り:部分和が target を超える場合はその選択をスキップする
|
||||
if (total + choices[i] > target) {
|
||||
continue
|
||||
}
|
||||
// 試行:選択を行い、要素と total を更新する
|
||||
state.add(choices[i])
|
||||
// 次の選択へ進む
|
||||
backtrack(state, target, total + choices[i], choices, res)
|
||||
// バックトラック:選択を取り消し、前の状態に戻す
|
||||
state.removeAt(state.size - 1)
|
||||
}
|
||||
}
|
||||
|
||||
/* 部分和 I を解く(重複部分集合を含む) */
|
||||
fun subsetSumINaive(nums: IntArray, target: Int): MutableList<MutableList<Int>?> {
|
||||
val state = mutableListOf<Int>() // 状態(部分集合)
|
||||
val total = 0 // 部分和
|
||||
val res = mutableListOf<MutableList<Int>?>() // 結果リスト(部分集合のリスト)
|
||||
backtrack(state, target, total, nums, res)
|
||||
return res
|
||||
}
|
||||
|
||||
/* Driver Code */
|
||||
fun main() {
|
||||
val nums = intArrayOf(3, 4, 5)
|
||||
val target = 9
|
||||
val res = subsetSumINaive(nums, target)
|
||||
|
||||
println("入力配列 nums = ${nums.contentToString()}, target = $target")
|
||||
println("和が $target に等しいすべての部分集合 res = $res")
|
||||
println("この方法の出力結果には重複した集合が含まれるので注意")
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
/**
|
||||
* File: subset_sum_ii.kt
|
||||
* Created Time: 2024-01-25
|
||||
* Author: curtishd (1023632660@qq.com)
|
||||
*/
|
||||
|
||||
package chapter_backtracking.subset_sum_ii
|
||||
|
||||
/* バックトラッキング:部分和 II */
|
||||
fun backtrack(
|
||||
state: MutableList<Int>,
|
||||
target: Int,
|
||||
choices: IntArray,
|
||||
start: Int,
|
||||
res: MutableList<MutableList<Int>?>
|
||||
) {
|
||||
// 部分集合の和が target に等しければ、解を記録
|
||||
if (target == 0) {
|
||||
res.add(state.toMutableList())
|
||||
return
|
||||
}
|
||||
// すべての選択肢を走査
|
||||
// 枝刈り 2: start から走査し、重複する部分集合の生成を避ける
|
||||
// 枝刈り 3: start から走査し、同じ要素の重複選択を避ける
|
||||
for (i in start..<choices.size) {
|
||||
// 枝刈り1:部分集合の和が target を超えたら、直ちにループを終了する
|
||||
// 配列はソート済みで後続要素のほうが大きく、部分集合の和は必ず target を超えるため
|
||||
if (target - choices[i] < 0) {
|
||||
break
|
||||
}
|
||||
// 枝刈り4:この要素が左隣の要素と等しければ、その探索分岐は重複しているためスキップする
|
||||
if (i > start && choices[i] == choices[i - 1]) {
|
||||
continue
|
||||
}
|
||||
// 試す:選択を行い、target と start を更新
|
||||
state.add(choices[i])
|
||||
// 次の選択へ進む
|
||||
backtrack(state, target - choices[i], choices, i + 1, res)
|
||||
// バックトラック:選択を取り消し、前の状態に戻す
|
||||
state.removeAt(state.size - 1)
|
||||
}
|
||||
}
|
||||
|
||||
/* 部分和 II を解く */
|
||||
fun subsetSumII(nums: IntArray, target: Int): MutableList<MutableList<Int>?> {
|
||||
val state = mutableListOf<Int>() // 状態(部分集合)
|
||||
nums.sort() // nums をソート
|
||||
val start = 0 // 開始点を走査
|
||||
val res = mutableListOf<MutableList<Int>?>() // 結果リスト(部分集合のリスト)
|
||||
backtrack(state, target, nums, start, res)
|
||||
return res
|
||||
}
|
||||
|
||||
/* Driver Code */
|
||||
fun main() {
|
||||
val nums = intArrayOf(4, 4, 5)
|
||||
val target = 9
|
||||
val res = subsetSumII(nums, target)
|
||||
|
||||
println("入力配列 nums = ${nums.contentToString()}, target = $target")
|
||||
println("和が $target に等しいすべての部分集合 res = $res")
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
/**
|
||||
* File: iteration.kt
|
||||
* Created Time: 2024-01-25
|
||||
* Author: curtishd (1023632660@qq.com)
|
||||
*/
|
||||
|
||||
package chapter_computational_complexity.iteration
|
||||
|
||||
/* for ループ */
|
||||
fun forLoop(n: Int): Int {
|
||||
var res = 0
|
||||
// 1, 2, ..., n-1, n を順に加算する
|
||||
for (i in 1..n) {
|
||||
res += i
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
/* while ループ */
|
||||
fun whileLoop(n: Int): Int {
|
||||
var res = 0
|
||||
var i = 1 // 条件変数を初期化する
|
||||
// 1, 2, ..., n-1, n を順に加算する
|
||||
while (i <= n) {
|
||||
res += i
|
||||
i++ // 条件変数を更新する
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
/* while ループ(2回更新) */
|
||||
fun whileLoopII(n: Int): Int {
|
||||
var res = 0
|
||||
var i = 1 // 条件変数を初期化する
|
||||
// 1, 4, 10, ... を順に加算する
|
||||
while (i <= n) {
|
||||
res += i
|
||||
// 条件変数を更新する
|
||||
i++
|
||||
i *= 2
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
/* 二重 for ループ */
|
||||
fun nestedForLoop(n: Int): String {
|
||||
val res = StringBuilder()
|
||||
// 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.toString()
|
||||
}
|
||||
|
||||
/* Driver Code */
|
||||
fun main() {
|
||||
val n = 5
|
||||
var res: Int
|
||||
|
||||
res = forLoop(n)
|
||||
println("\nfor ループの合計結果 res = $res")
|
||||
|
||||
res = whileLoop(n)
|
||||
println("\nwhile ループの合計結果 res = $res")
|
||||
|
||||
res = whileLoopII(n)
|
||||
println("\nwhile ループ (2 回更新) の合計結果 res = $res")
|
||||
|
||||
val resStr = nestedForLoop(n)
|
||||
println("\n二重 for ループの走査結果 $resStr")
|
||||
}
|
||||
@@ -0,0 +1,78 @@
|
||||
/**
|
||||
* File: recursion.kt
|
||||
* Created Time: 2024-01-25
|
||||
* Author: curtishd (1023632660@qq.com)
|
||||
*/
|
||||
|
||||
package chapter_computational_complexity.recursion
|
||||
|
||||
import java.util.*
|
||||
|
||||
/* 再帰 */
|
||||
fun recur(n: Int): Int {
|
||||
// 終了条件
|
||||
if (n == 1)
|
||||
return 1
|
||||
// 再帰: 再帰呼び出し
|
||||
val res = recur(n - 1)
|
||||
// 戻る: 結果を返す
|
||||
return n + res
|
||||
}
|
||||
|
||||
/* 反復で再帰を模擬する */
|
||||
fun forLoopRecur(n: Int): Int {
|
||||
// 明示的なスタックを使ってシステムコールスタックを模擬する
|
||||
val stack = Stack<Int>()
|
||||
var res = 0
|
||||
// 再帰: 再帰呼び出し
|
||||
for (i in n downTo 0) {
|
||||
// 「スタックへのプッシュ」で「再帰」を模擬する
|
||||
stack.push(i)
|
||||
}
|
||||
// 戻る: 結果を返す
|
||||
while (stack.isNotEmpty()) {
|
||||
// 「スタックから取り出す操作」で「帰り」をシミュレート
|
||||
res += stack.pop()
|
||||
}
|
||||
// res = 1+2+3+...+n
|
||||
return res
|
||||
}
|
||||
|
||||
/* 末尾再帰 */
|
||||
tailrec fun tailRecur(n: Int, res: Int): Int {
|
||||
// `tailrec` キーワードを追加して末尾再帰最適化を有効にする
|
||||
// 終了条件
|
||||
if (n == 0)
|
||||
return res
|
||||
// 末尾再帰呼び出し
|
||||
return tailRecur(n - 1, res + n)
|
||||
}
|
||||
|
||||
/* フィボナッチ数列:再帰 */
|
||||
fun 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) を再帰的に呼び出す
|
||||
val res = fib(n - 1) + fib(n - 2)
|
||||
// 結果 f(n) を返す
|
||||
return res
|
||||
}
|
||||
|
||||
/* Driver Code */
|
||||
fun main() {
|
||||
val n = 5
|
||||
var res: Int
|
||||
|
||||
res = recur(n)
|
||||
println("\n再帰関数の合計結果 res = $res")
|
||||
|
||||
res = forLoopRecur(n)
|
||||
println("\n反復で再帰をシミュレートした合計結果 res = $res")
|
||||
|
||||
res = tailRecur(n, 0)
|
||||
println("\n末尾再帰関数の合計結果 res = $res")
|
||||
|
||||
res = fib(n)
|
||||
println("\nフィボナッチ数列の第 $n 項は $res")
|
||||
}
|
||||
@@ -0,0 +1,109 @@
|
||||
/**
|
||||
* File: space_complexity.kt
|
||||
* Created Time: 2024-01-25
|
||||
* Author: curtishd (1023632660@qq.com)
|
||||
*/
|
||||
|
||||
package chapter_computational_complexity.space_complexity
|
||||
|
||||
import utils.ListNode
|
||||
import utils.TreeNode
|
||||
import utils.printTree
|
||||
|
||||
/* 関数 */
|
||||
fun function(): Int {
|
||||
// 何らかの処理を行う
|
||||
return 0
|
||||
}
|
||||
|
||||
/* 定数階 */
|
||||
fun constant(n: Int) {
|
||||
// 定数、変数、オブジェクトは O(1) の空間を占める
|
||||
val a = 0
|
||||
var b = 0
|
||||
val nums = Array(10000) { 0 }
|
||||
val node = ListNode(0)
|
||||
// ループ内の変数は O(1) の空間を占める
|
||||
for (i in 0..<n) {
|
||||
val c = 0
|
||||
}
|
||||
// ループ内の関数は O(1) の空間を占める
|
||||
for (i in 0..<n) {
|
||||
function()
|
||||
}
|
||||
}
|
||||
|
||||
/* 線形階 */
|
||||
fun linear(n: Int) {
|
||||
// 長さ n の配列は O(n) の空間を使用
|
||||
val nums = Array(n) { 0 }
|
||||
// 長さ n のリストは O(n) の空間を使用
|
||||
val nodes = mutableListOf<ListNode>()
|
||||
for (i in 0..<n) {
|
||||
nodes.add(ListNode(i))
|
||||
}
|
||||
// 長さ n のハッシュテーブルは O(n) の空間を使用
|
||||
val map = mutableMapOf<Int, String>()
|
||||
for (i in 0..<n) {
|
||||
map[i] = i.toString()
|
||||
}
|
||||
}
|
||||
|
||||
/* 線形時間(再帰実装) */
|
||||
fun linearRecur(n: Int) {
|
||||
println("再帰 n = $n")
|
||||
if (n == 1)
|
||||
return
|
||||
linearRecur(n - 1)
|
||||
}
|
||||
|
||||
/* 二乗階 */
|
||||
fun quadratic(n: Int) {
|
||||
// 行列は O(n^2) の空間を使用する
|
||||
val numMatrix = arrayOfNulls<Array<Int>?>(n)
|
||||
// 二次元リストは O(n^2) の空間を使用
|
||||
val numList = mutableListOf<MutableList<Int>>()
|
||||
for (i in 0..<n) {
|
||||
val tmp = mutableListOf<Int>()
|
||||
for (j in 0..<n) {
|
||||
tmp.add(0)
|
||||
}
|
||||
numList.add(tmp)
|
||||
}
|
||||
}
|
||||
|
||||
/* 二次時間(再帰実装) */
|
||||
tailrec fun quadraticRecur(n: Int): Int {
|
||||
if (n <= 0)
|
||||
return 0
|
||||
// 配列 nums の長さは n, n-1, ..., 2, 1
|
||||
val nums = Array(n) { 0 }
|
||||
println("再帰 n = $n における nums の長さ = ${nums.size}")
|
||||
return quadraticRecur(n - 1)
|
||||
}
|
||||
|
||||
/* 指数時間(完全二分木の構築) */
|
||||
fun buildTree(n: Int): TreeNode? {
|
||||
if (n == 0)
|
||||
return null
|
||||
val root = TreeNode(0)
|
||||
root.left = buildTree(n - 1)
|
||||
root.right = buildTree(n - 1)
|
||||
return root
|
||||
}
|
||||
|
||||
/* Driver Code */
|
||||
fun main() {
|
||||
val n = 5
|
||||
// 定数階
|
||||
constant(n)
|
||||
// 線形階
|
||||
linear(n)
|
||||
linearRecur(n)
|
||||
// 二乗階
|
||||
quadratic(n)
|
||||
quadraticRecur(n)
|
||||
// 指数オーダー
|
||||
val root = buildTree(n)
|
||||
printTree(root)
|
||||
}
|
||||
@@ -0,0 +1,168 @@
|
||||
/**
|
||||
* File: time_complexity.kt
|
||||
* Created Time: 2024-01-25
|
||||
* Author: curtishd (1023632660@qq.com)
|
||||
*/
|
||||
|
||||
package chapter_computational_complexity.time_complexity
|
||||
|
||||
/* 定数階 */
|
||||
fun constant(n: Int): Int {
|
||||
var count = 0
|
||||
val size = 100000
|
||||
for (i in 0..<size)
|
||||
count++
|
||||
return count
|
||||
}
|
||||
|
||||
/* 線形階 */
|
||||
fun linear(n: Int): Int {
|
||||
var count = 0
|
||||
for (i in 0..<n)
|
||||
count++
|
||||
return count
|
||||
}
|
||||
|
||||
/* 線形時間(配列を走査) */
|
||||
fun arrayTraversal(nums: IntArray): Int {
|
||||
var count = 0
|
||||
// ループ回数は配列長に比例する
|
||||
for (num in nums) {
|
||||
count++
|
||||
}
|
||||
return count
|
||||
}
|
||||
|
||||
/* 二乗階 */
|
||||
fun quadratic(n: Int): Int {
|
||||
var count = 0
|
||||
// ループ回数はデータサイズ n の二乗に比例する
|
||||
for (i in 0..<n) {
|
||||
for (j in 0..<n) {
|
||||
count++
|
||||
}
|
||||
}
|
||||
return count
|
||||
}
|
||||
|
||||
/* 二次時間(バブルソート) */
|
||||
fun bubbleSort(nums: IntArray): Int {
|
||||
var count = 0 // カウンタ
|
||||
// 外側のループ:未ソート区間は [0, i]
|
||||
for (i in nums.size - 1 downTo 1) {
|
||||
// 内側のループ:未ソート区間 [0, i] の最大要素をその区間の最右端へ交換
|
||||
for (j in 0..<i) {
|
||||
if (nums[j] > nums[j + 1]) {
|
||||
// nums[j] と nums[j + 1] を交換
|
||||
val temp = nums[j]
|
||||
nums[j] = nums[j + 1]
|
||||
nums[j + 1] = temp
|
||||
count += 3 // 要素交換には 3 回の単位操作が含まれる
|
||||
}
|
||||
}
|
||||
}
|
||||
return count
|
||||
}
|
||||
|
||||
/* 指数時間(ループ実装) */
|
||||
fun exponential(n: Int): Int {
|
||||
var count = 0
|
||||
var base = 1
|
||||
// 細胞は各ラウンドで 2 つに分裂し、数列 1, 2, 4, 8, ..., 2^(n-1) を形成する
|
||||
for (i in 0..<n) {
|
||||
for (j in 0..<base) {
|
||||
count++
|
||||
}
|
||||
base *= 2
|
||||
}
|
||||
// count = 1 + 2 + 4 + 8 + .. + 2^(n-1) = 2^n - 1
|
||||
return count
|
||||
}
|
||||
|
||||
/* 指数時間(再帰実装) */
|
||||
fun expRecur(n: Int): Int {
|
||||
if (n == 1) {
|
||||
return 1
|
||||
}
|
||||
return expRecur(n - 1) + expRecur(n - 1) + 1
|
||||
}
|
||||
|
||||
/* 対数時間(ループ実装) */
|
||||
fun logarithmic(n: Int): Int {
|
||||
var n1 = n
|
||||
var count = 0
|
||||
while (n1 > 1) {
|
||||
n1 /= 2
|
||||
count++
|
||||
}
|
||||
return count
|
||||
}
|
||||
|
||||
/* 対数時間(再帰実装) */
|
||||
fun logRecur(n: Int): Int {
|
||||
if (n <= 1)
|
||||
return 0
|
||||
return logRecur(n / 2) + 1
|
||||
}
|
||||
|
||||
/* 線形対数時間 */
|
||||
fun linearLogRecur(n: Int): Int {
|
||||
if (n <= 1)
|
||||
return 1
|
||||
var count = linearLogRecur(n / 2) + linearLogRecur(n / 2)
|
||||
for (i in 0..<n) {
|
||||
count++
|
||||
}
|
||||
return count
|
||||
}
|
||||
|
||||
/* 階乗時間(再帰実装) */
|
||||
fun factorialRecur(n: Int): Int {
|
||||
if (n == 0)
|
||||
return 1
|
||||
var count = 0
|
||||
// 1個から n 個に分裂
|
||||
for (i in 0..<n) {
|
||||
count += factorialRecur(n - 1)
|
||||
}
|
||||
return count
|
||||
}
|
||||
|
||||
/* Driver Code */
|
||||
fun main() {
|
||||
// n を変えて実行し、各計算量で操作回数がどう変化するかを確認できる
|
||||
val n = 8
|
||||
println("入力データサイズ n = $n")
|
||||
|
||||
var count = constant(n)
|
||||
println("定数時間の操作回数 = $count")
|
||||
|
||||
count = linear(n)
|
||||
println("線形時間の操作回数 = $count")
|
||||
count = arrayTraversal(IntArray(n))
|
||||
println("線形時間(配列走査)の操作回数 = $count")
|
||||
|
||||
count = quadratic(n)
|
||||
println("二乗時間の操作回数 = $count")
|
||||
val nums = IntArray(n)
|
||||
for (i in 0..<n)
|
||||
nums[i] = n - i // [n,n-1,...,2,1]
|
||||
count = bubbleSort(nums)
|
||||
println("二乗時間(バブルソート)の操作回数 = $count")
|
||||
|
||||
count = exponential(n)
|
||||
println("指数時間(ループ実装)の操作回数 = $count")
|
||||
count = expRecur(n)
|
||||
println("指数時間(再帰実装)の操作回数 = $count")
|
||||
|
||||
count = logarithmic(n)
|
||||
println("対数時間(ループ実装)の操作回数 = $count")
|
||||
count = logRecur(n)
|
||||
println("対数階(再帰実装)の操作回数 = $count")
|
||||
|
||||
count = linearLogRecur(n)
|
||||
println("線形対数階(再帰実装)の操作回数 = $count")
|
||||
|
||||
count = factorialRecur(n)
|
||||
println("階乗階(再帰実装)の操作回数 = $count")
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
/**
|
||||
* File: worst_best_time_complexity.kt
|
||||
* Created Time: 2024-01-25
|
||||
* Author: curtishd (1023632660@qq.com)
|
||||
*/
|
||||
|
||||
package chapter_computational_complexity.worst_best_time_complexity
|
||||
|
||||
/* 要素が { 1, 2, ..., n } で、順序がシャッフルされた配列を生成 */
|
||||
fun randomNumbers(n: Int): Array<Int?> {
|
||||
val nums = IntArray(n)
|
||||
// 配列 nums = { 1, 2, 3, ..., n } を生成
|
||||
for (i in 0..<n) {
|
||||
nums[i] = i + 1
|
||||
}
|
||||
// 配列要素をランダムにシャッフル
|
||||
nums.shuffle()
|
||||
val res = arrayOfNulls<Int>(n)
|
||||
for (i in 0..<n) {
|
||||
res[i] = nums[i]
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
/* 配列 nums 内で数値 1 のインデックスを探す */
|
||||
fun findOne(nums: Array<Int?>): Int {
|
||||
for (i in nums.indices) {
|
||||
// 要素 1 が配列の先頭にあるとき、最良時間計算量 O(1) となる
|
||||
// 要素 1 が配列の末尾にあるとき、最悪時間計算量 O(n) となる
|
||||
if (nums[i] == 1)
|
||||
return i
|
||||
}
|
||||
return -1
|
||||
}
|
||||
|
||||
/* Driver Code */
|
||||
fun main() {
|
||||
for (i in 0..9) {
|
||||
val n = 100
|
||||
val nums = randomNumbers(n)
|
||||
val index = findOne(nums)
|
||||
println("\n配列 [ 1, 2, ..., n ] をシャッフルした後 = ${nums.contentToString()}")
|
||||
println("数字 1 のインデックスは $index")
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
/**
|
||||
* File: binary_search_recur.kt
|
||||
* Created Time: 2024-01-25
|
||||
* Author: curtishd (1023632660@qq.com)
|
||||
*/
|
||||
|
||||
package chapter_divide_and_conquer.binary_search_recur
|
||||
|
||||
/* 二分探索:問題 f(i, j) */
|
||||
fun dfs(
|
||||
nums: IntArray,
|
||||
target: Int,
|
||||
i: Int,
|
||||
j: Int
|
||||
): Int {
|
||||
// 区間が空なら対象要素は存在しないので -1 を返す
|
||||
if (i > j) {
|
||||
return -1
|
||||
}
|
||||
// 中点インデックス m を計算
|
||||
val m = (i + j) / 2
|
||||
return if (nums[m] < target) {
|
||||
// 部分問題 f(m+1, j) を再帰的に解く
|
||||
dfs(nums, target, m + 1, j)
|
||||
} else if (nums[m] > target) {
|
||||
// 部分問題 f(i, m-1) を再帰的に解く
|
||||
dfs(nums, target, i, m - 1)
|
||||
} else {
|
||||
// 目標要素が見つかったらそのインデックスを返す
|
||||
m
|
||||
}
|
||||
}
|
||||
|
||||
/* 二分探索 */
|
||||
fun binarySearch(nums: IntArray, target: Int): Int {
|
||||
val n = nums.size
|
||||
// 問題 f(0, n-1) を解く
|
||||
return dfs(nums, target, 0, n - 1)
|
||||
}
|
||||
|
||||
/* Driver Code */
|
||||
fun main() {
|
||||
val target = 6
|
||||
val nums = intArrayOf(1, 3, 6, 8, 12, 15, 23, 26, 31, 35)
|
||||
|
||||
// 二分探索(両閉区間)
|
||||
val index = binarySearch(nums, target)
|
||||
println("対象要素 6 のインデックス = $index")
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
/**
|
||||
* File: build_tree.kt
|
||||
* Created Time: 2024-01-25
|
||||
* Author: curtishd (1023632660@qq.com)
|
||||
*/
|
||||
|
||||
package chapter_divide_and_conquer.build_tree
|
||||
|
||||
import utils.TreeNode
|
||||
import utils.printTree
|
||||
|
||||
/* 二分木を構築:分割統治 */
|
||||
fun dfs(
|
||||
preorder: IntArray,
|
||||
inorderMap: Map<Int?, Int?>,
|
||||
i: Int,
|
||||
l: Int,
|
||||
r: Int
|
||||
): TreeNode? {
|
||||
// 部分木区間が空なら終了する
|
||||
if (r - l < 0) return null
|
||||
// ルートノードを初期化する
|
||||
val root = TreeNode(preorder[i])
|
||||
// m を求めて左右部分木を分割する
|
||||
val m = inorderMap[preorder[i]]!!
|
||||
// 部分問題:左部分木を構築する
|
||||
root.left = dfs(preorder, inorderMap, i + 1, l, m - 1)
|
||||
// 部分問題:右部分木を構築する
|
||||
root.right = dfs(preorder, inorderMap, i + 1 + m - l, m + 1, r)
|
||||
// 根ノードを返す
|
||||
return root
|
||||
}
|
||||
|
||||
/* 二分木を構築 */
|
||||
fun buildTree(preorder: IntArray, inorder: IntArray): TreeNode? {
|
||||
// inorder の要素からインデックスへの対応を格納するハッシュテーブルを初期化する
|
||||
val inorderMap = HashMap<Int?, Int?>()
|
||||
for (i in inorder.indices) {
|
||||
inorderMap[inorder[i]] = i
|
||||
}
|
||||
val root = dfs(preorder, inorderMap, 0, 0, inorder.size - 1)
|
||||
return root
|
||||
}
|
||||
|
||||
/* Driver Code */
|
||||
fun main() {
|
||||
val preorder = intArrayOf(3, 9, 2, 1, 7)
|
||||
val inorder = intArrayOf(9, 3, 1, 2, 7)
|
||||
println("前順走査 = ${preorder.contentToString()}")
|
||||
println("中順走査 = ${inorder.contentToString()}")
|
||||
|
||||
val root = buildTree(preorder, inorder)
|
||||
println("構築した二分木:")
|
||||
printTree(root)
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
/**
|
||||
* File: hanota.kt
|
||||
* Created Time: 2024-01-25
|
||||
* Author: curtishd (1023632660@qq.com)
|
||||
*/
|
||||
|
||||
package chapter_divide_and_conquer.hanota
|
||||
|
||||
/* 円盤を 1 枚移動 */
|
||||
fun move(src: MutableList<Int>, tar: MutableList<Int>) {
|
||||
// src の上から円盤を1枚取り出す
|
||||
val pan = src.removeAt(src.size - 1)
|
||||
// 円盤を tar の上に置く
|
||||
tar.add(pan)
|
||||
}
|
||||
|
||||
/* ハノイの塔の問題 f(i) を解く */
|
||||
fun dfs(i: Int, src: MutableList<Int>, buf: MutableList<Int>, tar: MutableList<Int>) {
|
||||
// src に円盤が 1 枚だけ残っている場合は、そのまま tar へ移す
|
||||
if (i == 1) {
|
||||
move(src, tar)
|
||||
return
|
||||
}
|
||||
// 部分問題 f(i-1):src の上部 i-1 枚の円盤を tar を補助にして buf へ移す
|
||||
dfs(i - 1, src, tar, buf)
|
||||
// 部分問題 f(1):src に残る 1 枚の円盤を tar に移す
|
||||
move(src, tar)
|
||||
// 部分問題 f(i-1):buf の上部 i-1 枚の円盤を src を補助にして tar へ移す
|
||||
dfs(i - 1, buf, src, tar)
|
||||
}
|
||||
|
||||
/* ハノイの塔を解く */
|
||||
fun solveHanota(A: MutableList<Int>, B: MutableList<Int>, C: MutableList<Int>) {
|
||||
val n = A.size
|
||||
// A の上から n 枚の円盤を B を介して C へ移す
|
||||
dfs(n, A, B, C)
|
||||
}
|
||||
|
||||
/* Driver Code */
|
||||
fun main() {
|
||||
// リスト末尾が柱の頂上
|
||||
val A = mutableListOf(5, 4, 3, 2, 1)
|
||||
val B = mutableListOf<Int>()
|
||||
val C = mutableListOf<Int>()
|
||||
println("初期状態:")
|
||||
println("A = $A")
|
||||
println("B = $B")
|
||||
println("C = $C")
|
||||
|
||||
solveHanota(A, B, C)
|
||||
|
||||
println("円盤の移動完了後:")
|
||||
println("A = $A")
|
||||
println("B = $B")
|
||||
println("C = $C")
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
/**
|
||||
* File: climbing_stairs_backtrack.kt
|
||||
* Created Time: 2024-01-25
|
||||
* Author: curtishd (1023632660@qq.com)
|
||||
*/
|
||||
|
||||
package chapter_dynamic_programming
|
||||
|
||||
/* バックトラッキング */
|
||||
fun backtrack(
|
||||
choices: MutableList<Int>,
|
||||
state: Int,
|
||||
n: Int,
|
||||
res: MutableList<Int>
|
||||
) {
|
||||
// 第 n 段に到達したら、方法数を 1 増やす
|
||||
if (state == n)
|
||||
res[0] = res[0] + 1
|
||||
// すべての選択肢を走査
|
||||
for (choice in choices) {
|
||||
// 枝刈り: 第 n 段を超えないようにする
|
||||
if (state + choice > n) continue
|
||||
// 試行: 選択を行い、状態を更新
|
||||
backtrack(choices, state + choice, n, res)
|
||||
// バックトラック
|
||||
}
|
||||
}
|
||||
|
||||
/* 階段登り:バックトラッキング */
|
||||
fun climbingStairsBacktrack(n: Int): Int {
|
||||
val choices = mutableListOf(1, 2) // 1 段または 2 段上ることを選べる
|
||||
val state = 0 // 第 0 段から上り始める
|
||||
val res = mutableListOf<Int>()
|
||||
res.add(0) // res[0] を使って方法数を記録する
|
||||
backtrack(choices, state, n, res)
|
||||
return res[0]
|
||||
}
|
||||
|
||||
/* Driver Code */
|
||||
fun main() {
|
||||
val n = 9
|
||||
|
||||
val res = climbingStairsBacktrack(n)
|
||||
println("$n 段の階段の登り方は全部で $res 通り")
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
/**
|
||||
* File: climbing_stairs_constraint_dp.kt
|
||||
* Created Time: 2024-01-25
|
||||
* Author: curtishd (1023632660@qq.com)
|
||||
*/
|
||||
|
||||
package chapter_dynamic_programming
|
||||
|
||||
/* 制約付き階段登り:動的計画法 */
|
||||
fun climbingStairsConstraintDP(n: Int): Int {
|
||||
if (n == 1 || n == 2) {
|
||||
return 1
|
||||
}
|
||||
// 部分問題の解を保存するために dp テーブルを初期化
|
||||
val dp = Array(n + 1) { IntArray(3) }
|
||||
// 初期状態:最小部分問題の解をあらかじめ設定
|
||||
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]
|
||||
}
|
||||
|
||||
/* Driver Code */
|
||||
fun main() {
|
||||
val n = 9
|
||||
|
||||
val res = climbingStairsConstraintDP(n)
|
||||
println("$n 段の階段の登り方は全部で $res 通り")
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
/**
|
||||
* File: climbing_stairs_dfs.kt
|
||||
* Created Time: 2024-01-25
|
||||
* Author: curtishd (1023632660@qq.com)
|
||||
*/
|
||||
|
||||
package chapter_dynamic_programming
|
||||
|
||||
/* 検索 */
|
||||
fun dfs(i: Int): Int {
|
||||
// dp[1] と dp[2] は既知なので返す
|
||||
if (i == 1 || i == 2) return i
|
||||
// dp[i] = dp[i-1] + dp[i-2]
|
||||
val count = dfs(i - 1) + dfs(i - 2)
|
||||
return count
|
||||
}
|
||||
|
||||
/* 階段登り:探索 */
|
||||
fun climbingStairsDFS(n: Int): Int {
|
||||
return dfs(n)
|
||||
}
|
||||
|
||||
/* Driver Code */
|
||||
fun main() {
|
||||
val n = 9
|
||||
|
||||
val res = climbingStairsDFS(n)
|
||||
println("$n 段の階段の登り方は全部で $res 通り")
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
/**
|
||||
* File: climbing_stairs_dfs_mem.kt
|
||||
* Created Time: 2024-01-25
|
||||
* Author: curtishd (1023632660@qq.com)
|
||||
*/
|
||||
|
||||
package chapter_dynamic_programming
|
||||
|
||||
/* メモ化探索 */
|
||||
fun dfs(i: Int, mem: IntArray): 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]
|
||||
val count = dfs(i - 1, mem) + dfs(i - 2, mem)
|
||||
// dp[i] を記録する
|
||||
mem[i] = count
|
||||
return count
|
||||
}
|
||||
|
||||
/* 階段登り:メモ化探索 */
|
||||
fun climbingStairsDFSMem(n: Int): Int {
|
||||
// mem[i] は第 i 段まで上る方法の総数を記録し、-1 は未記録を表す
|
||||
val mem = IntArray(n + 1)
|
||||
mem.fill(-1)
|
||||
return dfs(n, mem)
|
||||
}
|
||||
|
||||
/* Driver Code */
|
||||
fun main() {
|
||||
val n = 9
|
||||
|
||||
val res = climbingStairsDFSMem(n)
|
||||
println("$n 段の階段の登り方は全部で $res 通り")
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
/**
|
||||
* File: climbing_stairs_dp.kt
|
||||
* Created Time: 2024-01-25
|
||||
* Author: curtishd (1023632660@qq.com)
|
||||
*/
|
||||
|
||||
package chapter_dynamic_programming
|
||||
|
||||
/* 階段登り:動的計画法 */
|
||||
fun climbingStairsDP(n: Int): Int {
|
||||
if (n == 1 || n == 2) return n
|
||||
// 部分問題の解を保存するために dp テーブルを初期化
|
||||
val dp = IntArray(n + 1)
|
||||
// 初期状態:最小部分問題の解をあらかじめ設定
|
||||
dp[1] = 1
|
||||
dp[2] = 2
|
||||
// 状態遷移:小さい部分問題から大きい部分問題へ順に解く
|
||||
for (i in 3..n) {
|
||||
dp[i] = dp[i - 1] + dp[i - 2]
|
||||
}
|
||||
return dp[n]
|
||||
}
|
||||
|
||||
/* 階段登り:空間最適化した動的計画法 */
|
||||
fun climbingStairsDPComp(n: Int): Int {
|
||||
if (n == 1 || n == 2) return n
|
||||
var a = 1
|
||||
var b = 2
|
||||
for (i in 3..n) {
|
||||
val temp = b
|
||||
b += a
|
||||
a = temp
|
||||
}
|
||||
return b
|
||||
}
|
||||
|
||||
/* Driver Code */
|
||||
fun main() {
|
||||
val n = 9
|
||||
|
||||
var res = climbingStairsDP(n)
|
||||
println("$n 段の階段の登り方は全部で $res 通り")
|
||||
|
||||
res = climbingStairsDPComp(n)
|
||||
println("$n 段の階段の登り方は全部で $res 通り")
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
/**
|
||||
* File: coin_change.kt
|
||||
* Created Time: 2024-01-25
|
||||
* Author: curtishd (1023632660@qq.com)
|
||||
*/
|
||||
|
||||
package chapter_dynamic_programming
|
||||
|
||||
import kotlin.math.min
|
||||
|
||||
/* コイン両替:動的計画法 */
|
||||
fun coinChangeDP(coins: IntArray, amt: Int): Int {
|
||||
val n = coins.size
|
||||
val MAX = amt + 1
|
||||
// dp テーブルを初期化
|
||||
val dp = Array(n + 1) { IntArray(amt + 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 if (dp[n][amt] != MAX) dp[n][amt] else -1
|
||||
}
|
||||
|
||||
/* コイン交換:空間最適化後の動的計画法 */
|
||||
fun coinChangeDPComp(coins: IntArray, amt: Int): Int {
|
||||
val n = coins.size
|
||||
val MAX = amt + 1
|
||||
// dp テーブルを初期化
|
||||
val dp = IntArray(amt + 1)
|
||||
dp.fill(MAX)
|
||||
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 if (dp[amt] != MAX) dp[amt] else -1
|
||||
}
|
||||
|
||||
/* Driver Code */
|
||||
fun main() {
|
||||
val coins = intArrayOf(1, 2, 5)
|
||||
val amt = 4
|
||||
|
||||
// 動的計画法
|
||||
var res = coinChangeDP(coins, amt)
|
||||
println("目標金額を作るのに必要な最小硬貨枚数は $res")
|
||||
|
||||
// 空間最適化後の動的計画法
|
||||
res = coinChangeDPComp(coins, amt)
|
||||
println("目標金額を作るのに必要な最小硬貨枚数は $res")
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
/**
|
||||
* File: coin_change_ii.kt
|
||||
* Created Time: 2024-01-25
|
||||
* Author: curtishd (1023632660@qq.com)
|
||||
*/
|
||||
|
||||
package chapter_dynamic_programming
|
||||
|
||||
/* コイン両替 II:動的計画法 */
|
||||
fun coinChangeIIDP(coins: IntArray, amt: Int): Int {
|
||||
val n = coins.size
|
||||
// dp テーブルを初期化
|
||||
val dp = Array(n + 1) { IntArray(amt + 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:空間最適化した動的計画法 */
|
||||
fun coinChangeIIDPComp(coins: IntArray, amt: Int): Int {
|
||||
val n = coins.size
|
||||
// dp テーブルを初期化
|
||||
val dp = IntArray(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]
|
||||
}
|
||||
|
||||
/* Driver Code */
|
||||
fun main() {
|
||||
val coins = intArrayOf(1, 2, 5)
|
||||
val amt = 5
|
||||
|
||||
// 動的計画法
|
||||
var res = coinChangeIIDP(coins, amt)
|
||||
println("目標金額を作る硬貨の組み合わせ数は $res")
|
||||
|
||||
// 空間最適化後の動的計画法
|
||||
res = coinChangeIIDPComp(coins, amt)
|
||||
println("目標金額を作る硬貨の組み合わせ数は $res")
|
||||
}
|
||||
@@ -0,0 +1,143 @@
|
||||
/**
|
||||
* File: edit_distance.kt
|
||||
* Created Time: 2024-01-25
|
||||
* Author: curtishd (1023632660@qq.com)
|
||||
*/
|
||||
|
||||
package chapter_dynamic_programming
|
||||
|
||||
import kotlin.math.min
|
||||
|
||||
/* 編集距離:総当たり探索 */
|
||||
fun editDistanceDFS(
|
||||
s: String,
|
||||
t: String,
|
||||
i: Int,
|
||||
j: Int
|
||||
): Int {
|
||||
// s と t がともに空なら 0 を返す
|
||||
if (i == 0 && j == 0) return 0
|
||||
// s が空なら t の長さを返す
|
||||
if (i == 0) return j
|
||||
// t が空なら s の長さを返す
|
||||
if (j == 0) return i
|
||||
// 2 つの文字が等しければ、その 2 文字をそのままスキップする
|
||||
if (s[i - 1] == t[j - 1]) return editDistanceDFS(s, t, i - 1, j - 1)
|
||||
// 最小編集回数 = 挿入・削除・置換の 3 操作における最小編集回数 + 1
|
||||
val insert = editDistanceDFS(s, t, i, j - 1)
|
||||
val delete = editDistanceDFS(s, t, i - 1, j)
|
||||
val replace = editDistanceDFS(s, t, i - 1, j - 1)
|
||||
// 最小編集回数を返す
|
||||
return min(min(insert, delete), replace) + 1
|
||||
}
|
||||
|
||||
/* 編集距離:メモ化探索 */
|
||||
fun editDistanceDFSMem(
|
||||
s: String,
|
||||
t: String,
|
||||
mem: Array<IntArray>,
|
||||
i: Int,
|
||||
j: Int
|
||||
): Int {
|
||||
// s と t がともに空なら 0 を返す
|
||||
if (i == 0 && j == 0) return 0
|
||||
// s が空なら t の長さを返す
|
||||
if (i == 0) return j
|
||||
// t が空なら s の長さを返す
|
||||
if (j == 0) return i
|
||||
// 記録済みなら、それをそのまま返す
|
||||
if (mem[i][j] != -1) return mem[i][j]
|
||||
// 2 つの文字が等しければ、その 2 文字をそのままスキップする
|
||||
if (s[i - 1] == t[j - 1]) return editDistanceDFSMem(s, t, mem, i - 1, j - 1)
|
||||
// 最小編集回数 = 挿入・削除・置換の 3 操作における最小編集回数 + 1
|
||||
val insert = editDistanceDFSMem(s, t, mem, i, j - 1)
|
||||
val delete = editDistanceDFSMem(s, t, mem, i - 1, j)
|
||||
val replace = editDistanceDFSMem(s, t, mem, i - 1, j - 1)
|
||||
// 最小編集回数を記録して返す
|
||||
mem[i][j] = min(min(insert, delete), replace) + 1
|
||||
return mem[i][j]
|
||||
}
|
||||
|
||||
/* 編集距離:動的計画法 */
|
||||
fun editDistanceDP(s: String, t: String): Int {
|
||||
val n = s.length
|
||||
val m = t.length
|
||||
val dp = Array(n + 1) { IntArray(m + 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[i - 1] == t[j - 1]) {
|
||||
// 2 つの文字が等しければ、その 2 文字をそのままスキップする
|
||||
dp[i][j] = dp[i - 1][j - 1]
|
||||
} else {
|
||||
// 最小編集回数 = 挿入・削除・置換の 3 操作における最小編集回数 + 1
|
||||
dp[i][j] = min(min(dp[i][j - 1], dp[i - 1][j]), dp[i - 1][j - 1]) + 1
|
||||
}
|
||||
}
|
||||
}
|
||||
return dp[n][m]
|
||||
}
|
||||
|
||||
/* 編集距離:空間最適化した動的計画法 */
|
||||
fun editDistanceDPComp(s: String, t: String): Int {
|
||||
val n = s.length
|
||||
val m = t.length
|
||||
val dp = IntArray(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) {
|
||||
val temp = dp[j]
|
||||
if (s[i - 1] == t[j - 1]) {
|
||||
// 2 つの文字が等しければ、その 2 文字をそのままスキップする
|
||||
dp[j] = leftup
|
||||
} else {
|
||||
// 最小編集回数 = 挿入・削除・置換の 3 操作における最小編集回数 + 1
|
||||
dp[j] = min(min(dp[j - 1], dp[j]), leftup) + 1
|
||||
}
|
||||
leftup = temp // 次の反復の dp[i-1, j-1] に更新する
|
||||
}
|
||||
}
|
||||
return dp[m]
|
||||
}
|
||||
|
||||
/* Driver Code */
|
||||
fun main() {
|
||||
val s = "bag"
|
||||
val t = "pack"
|
||||
val n = s.length
|
||||
val m = t.length
|
||||
|
||||
// 全探索
|
||||
var res = editDistanceDFS(s, t, n, m)
|
||||
println("$s を $t に変更するには最小で $res 回の編集が必要")
|
||||
|
||||
// メモ化探索
|
||||
val mem = Array(n + 1) { IntArray(m + 1) }
|
||||
for (row in mem)
|
||||
row.fill(-1)
|
||||
res = editDistanceDFSMem(s, t, mem, n, m)
|
||||
println("$s を $t に変更するには最小で $res 回の編集が必要")
|
||||
|
||||
// 動的計画法
|
||||
res = editDistanceDP(s, t)
|
||||
println("$s を $t に変更するには最小で $res 回の編集が必要")
|
||||
|
||||
// 空間最適化後の動的計画法
|
||||
res = editDistanceDPComp(s, t)
|
||||
println("$s を $t に変更するには最小で $res 回の編集が必要")
|
||||
}
|
||||
@@ -0,0 +1,125 @@
|
||||
/**
|
||||
* File: knapsack.kt
|
||||
* Created Time: 2024-01-25
|
||||
* Author: curtishd (1023632660@qq.com)
|
||||
*/
|
||||
|
||||
package chapter_dynamic_programming
|
||||
|
||||
import kotlin.math.max
|
||||
|
||||
/* 0-1 ナップサック:総当たり探索 */
|
||||
fun knapsackDFS(
|
||||
wgt: IntArray,
|
||||
_val: IntArray,
|
||||
i: Int,
|
||||
c: Int
|
||||
): Int {
|
||||
// すべての品物を選び終えたか、ナップサックに残り容量がなければ、価値 0 を返す
|
||||
if (i == 0 || c == 0) {
|
||||
return 0
|
||||
}
|
||||
// ナップサック容量を超える場合は、入れない選択しかできない
|
||||
if (wgt[i - 1] > c) {
|
||||
return knapsackDFS(wgt, _val, i - 1, c)
|
||||
}
|
||||
// 品物 i を入れない場合と入れる場合の最大価値を計算する
|
||||
val no = knapsackDFS(wgt, _val, i - 1, c)
|
||||
val yes = knapsackDFS(wgt, _val, i - 1, c - wgt[i - 1]) + _val[i - 1]
|
||||
// 2つの案のうち価値が大きいほうを返す
|
||||
return max(no, yes)
|
||||
}
|
||||
|
||||
/* 0-1 ナップサック:メモ化探索 */
|
||||
fun knapsackDFSMem(
|
||||
wgt: IntArray,
|
||||
_val: IntArray,
|
||||
mem: Array<IntArray>,
|
||||
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, _val, mem, i - 1, c)
|
||||
}
|
||||
// 品物 i を入れない場合と入れる場合の最大価値を計算する
|
||||
val no = knapsackDFSMem(wgt, _val, mem, i - 1, c)
|
||||
val yes = knapsackDFSMem(wgt, _val, mem, i - 1, c - wgt[i - 1]) + _val[i - 1]
|
||||
// 2 つの案のうち価値が大きい方を記録して返す
|
||||
mem[i][c] = max(no, yes)
|
||||
return mem[i][c]
|
||||
}
|
||||
|
||||
/* 0-1 ナップサック:動的計画法 */
|
||||
fun knapsackDP(wgt: IntArray, _val: IntArray, cap: Int): Int {
|
||||
val n = wgt.size
|
||||
// dp テーブルを初期化
|
||||
val dp = Array(n + 1) { IntArray(cap + 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 ナップサック:空間最適化後の動的計画法 */
|
||||
fun knapsackDPComp(wgt: IntArray, _val: IntArray, cap: Int): Int {
|
||||
val n = wgt.size
|
||||
// dp テーブルを初期化
|
||||
val dp = IntArray(cap + 1)
|
||||
// 状態遷移
|
||||
for (i in 1..n) {
|
||||
// 逆順に走査する
|
||||
for (c in cap downTo 1) {
|
||||
if (wgt[i - 1] <= c) {
|
||||
// 品物 i を選ばない場合と選ぶ場合の大きい方
|
||||
dp[c] = max(dp[c], dp[c - wgt[i - 1]] + _val[i - 1])
|
||||
}
|
||||
}
|
||||
}
|
||||
return dp[cap]
|
||||
}
|
||||
|
||||
/* Driver Code */
|
||||
fun main() {
|
||||
val wgt = intArrayOf(10, 20, 30, 40, 50)
|
||||
val _val = intArrayOf(50, 120, 150, 210, 240)
|
||||
val cap = 50
|
||||
val n = wgt.size
|
||||
|
||||
// 全探索
|
||||
var res = knapsackDFS(wgt, _val, n, cap)
|
||||
println("ナップサック容量を超えない最大価値は $res")
|
||||
|
||||
// メモ化探索
|
||||
val mem = Array(n + 1) { IntArray(cap + 1) }
|
||||
for (row in mem) {
|
||||
row.fill(-1)
|
||||
}
|
||||
res = knapsackDFSMem(wgt, _val, mem, n, cap)
|
||||
println("ナップサック容量を超えない最大価値は $res")
|
||||
|
||||
// 動的計画法
|
||||
res = knapsackDP(wgt, _val, cap)
|
||||
println("ナップサック容量を超えない最大価値は $res")
|
||||
|
||||
// 空間最適化後の動的計画法
|
||||
res = knapsackDPComp(wgt, _val, cap)
|
||||
println("ナップサック容量を超えない最大価値は $res")
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
/**
|
||||
* File: min_cost_climbing_stairs_dp.kt
|
||||
* Created Time: 2024-01-25
|
||||
* Author: curtishd (1023632660@qq.com)
|
||||
*/
|
||||
|
||||
package chapter_dynamic_programming
|
||||
|
||||
import kotlin.math.min
|
||||
|
||||
/* 階段登りの最小コスト:動的計画法 */
|
||||
fun minCostClimbingStairsDP(cost: IntArray): Int {
|
||||
val n = cost.size - 1
|
||||
if (n == 1 || n == 2) return cost[n]
|
||||
// 部分問題の解を保存するために dp テーブルを初期化
|
||||
val dp = IntArray(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]
|
||||
}
|
||||
|
||||
/* 階段昇りの最小コスト:空間最適化後の動的計画法 */
|
||||
fun minCostClimbingStairsDPComp(cost: IntArray): Int {
|
||||
val n = cost.size - 1
|
||||
if (n == 1 || n == 2) return cost[n]
|
||||
var a = cost[1]
|
||||
var b = cost[2]
|
||||
for (i in 3..n) {
|
||||
val tmp = b
|
||||
b = min(a, tmp) + cost[i]
|
||||
a = tmp
|
||||
}
|
||||
return b
|
||||
}
|
||||
|
||||
/* Driver Code */
|
||||
fun main() {
|
||||
val cost = intArrayOf(0, 1, 10, 1, 1, 1, 10, 1, 1, 10, 1)
|
||||
println("入力された階段コストのリストは ${cost.contentToString()}")
|
||||
|
||||
var res = minCostClimbingStairsDP(cost)
|
||||
println("階段を上り切る最小コストは $res")
|
||||
|
||||
res = minCostClimbingStairsDPComp(cost)
|
||||
println("階段を上り切る最小コストは $res")
|
||||
}
|
||||
@@ -0,0 +1,132 @@
|
||||
/**
|
||||
* File: min_path_sum.kt
|
||||
* Created Time: 2024-01-25
|
||||
* Author: curtishd (1023632660@qq.com)
|
||||
*/
|
||||
|
||||
package chapter_dynamic_programming
|
||||
|
||||
import kotlin.math.min
|
||||
|
||||
/* 最小経路和:全探索 */
|
||||
fun minPathSumDFS(grid: Array<IntArray>, i: Int, j: Int): Int {
|
||||
// 左上のセルなら探索を終了する
|
||||
if (i == 0 && j == 0) {
|
||||
return grid[0][0]
|
||||
}
|
||||
// 行または列のインデックスが範囲外なら、コスト +∞ を返す
|
||||
if (i < 0 || j < 0) {
|
||||
return Int.MAX_VALUE
|
||||
}
|
||||
// 左上から (i-1, j) および (i, j-1) までの最小経路コストを計算する
|
||||
val up = minPathSumDFS(grid, i - 1, j)
|
||||
val left = minPathSumDFS(grid, i, j - 1)
|
||||
// 左上隅から (i, j) までの最小経路コストを返す
|
||||
return min(left, up) + grid[i][j]
|
||||
}
|
||||
|
||||
/* 最小経路和:メモ化探索 */
|
||||
fun minPathSumDFSMem(
|
||||
grid: Array<IntArray>,
|
||||
mem: Array<IntArray>,
|
||||
i: Int,
|
||||
j: Int
|
||||
): Int {
|
||||
// 左上のセルなら探索を終了する
|
||||
if (i == 0 && j == 0) {
|
||||
return grid[0][0]
|
||||
}
|
||||
// 行または列のインデックスが範囲外なら、コスト +∞ を返す
|
||||
if (i < 0 || j < 0) {
|
||||
return Int.MAX_VALUE
|
||||
}
|
||||
// 既に記録があればそのまま返す
|
||||
if (mem[i][j] != -1) {
|
||||
return mem[i][j]
|
||||
}
|
||||
// 左と上のセルからの最小経路コスト
|
||||
val up = minPathSumDFSMem(grid, mem, i - 1, j)
|
||||
val left = minPathSumDFSMem(grid, mem, i, j - 1)
|
||||
// 左上から (i, j) までの最小経路コストを記録して返す
|
||||
mem[i][j] = min(left, up) + grid[i][j]
|
||||
return mem[i][j]
|
||||
}
|
||||
|
||||
/* 最小経路和:動的計画法 */
|
||||
fun minPathSumDP(grid: Array<IntArray>): Int {
|
||||
val n = grid.size
|
||||
val m = grid[0].size
|
||||
// dp テーブルを初期化
|
||||
val dp = Array(n) { IntArray(m) }
|
||||
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]
|
||||
}
|
||||
|
||||
/* 最小経路和:空間最適化後の動的計画法 */
|
||||
fun minPathSumDPComp(grid: Array<IntArray>): Int {
|
||||
val n = grid.size
|
||||
val m = grid[0].size
|
||||
// dp テーブルを初期化
|
||||
val dp = IntArray(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]
|
||||
}
|
||||
|
||||
/* Driver Code */
|
||||
fun main() {
|
||||
val grid = arrayOf(
|
||||
intArrayOf(1, 3, 1, 5),
|
||||
intArrayOf(2, 2, 4, 2),
|
||||
intArrayOf(5, 3, 2, 1),
|
||||
intArrayOf(4, 3, 5, 2)
|
||||
)
|
||||
val n = grid.size
|
||||
val m = grid[0].size
|
||||
|
||||
// 全探索
|
||||
var res = minPathSumDFS(grid, n - 1, m - 1)
|
||||
println("左上から右下までの最小経路和は $res")
|
||||
|
||||
// メモ化探索
|
||||
val mem = Array(n) { IntArray(m) }
|
||||
for (row in mem) {
|
||||
row.fill(-1)
|
||||
}
|
||||
res = minPathSumDFSMem(grid, mem, n - 1, m - 1)
|
||||
println("左上から右下までの最小経路和は $res")
|
||||
|
||||
// 動的計画法
|
||||
res = minPathSumDP(grid)
|
||||
println("左上から右下までの最小経路和は $res")
|
||||
|
||||
// 空間最適化後の動的計画法
|
||||
res = minPathSumDPComp(grid)
|
||||
println("左上から右下までの最小経路和は $res")
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
/**
|
||||
* File: unbounded_knapsack.kt
|
||||
* Created Time: 2024-01-25
|
||||
* Author: curtishd (1023632660@qq.com)
|
||||
*/
|
||||
|
||||
package chapter_dynamic_programming
|
||||
|
||||
import kotlin.math.max
|
||||
|
||||
/* 完全ナップサック問題:動的計画法 */
|
||||
fun unboundedKnapsackDP(wgt: IntArray, _val: IntArray, cap: Int): Int {
|
||||
val n = wgt.size
|
||||
// dp テーブルを初期化
|
||||
val dp = Array(n + 1) { IntArray(cap + 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]
|
||||
}
|
||||
|
||||
/* 完全ナップサック問題:空間最適化後の動的計画法 */
|
||||
fun unboundedKnapsackDPComp(
|
||||
wgt: IntArray,
|
||||
_val: IntArray,
|
||||
cap: Int
|
||||
): Int {
|
||||
val n = wgt.size
|
||||
// dp テーブルを初期化
|
||||
val dp = IntArray(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]
|
||||
}
|
||||
|
||||
/* Driver Code */
|
||||
fun main() {
|
||||
val wgt = intArrayOf(1, 2, 3)
|
||||
val _val = intArrayOf(5, 11, 15)
|
||||
val cap = 4
|
||||
|
||||
// 動的計画法
|
||||
var res = unboundedKnapsackDP(wgt, _val, cap)
|
||||
println("ナップサック容量を超えない最大価値は $res")
|
||||
|
||||
// 空間最適化後の動的計画法
|
||||
res = unboundedKnapsackDPComp(wgt, _val, cap)
|
||||
println("ナップサック容量を超えない最大価値は $res")
|
||||
}
|
||||
@@ -0,0 +1,121 @@
|
||||
/**
|
||||
* File: graph_adjacency_list.kt
|
||||
* Created Time: 2024-01-25
|
||||
* Author: curtishd (1023632660@qq.com)
|
||||
*/
|
||||
|
||||
package chapter_graph
|
||||
|
||||
import utils.Vertex
|
||||
|
||||
/* 隣接リストに基づく無向グラフクラス */
|
||||
class GraphAdjList(edges: Array<Array<Vertex?>>) {
|
||||
// 隣接リスト。key は頂点、value はその頂点に隣接する全頂点
|
||||
val adjList = HashMap<Vertex, MutableList<Vertex>>()
|
||||
|
||||
/* コンストラクタ */
|
||||
init {
|
||||
// すべての頂点と辺を追加
|
||||
for (edge in edges) {
|
||||
addVertex(edge[0]!!)
|
||||
addVertex(edge[1]!!)
|
||||
addEdge(edge[0]!!, edge[1]!!)
|
||||
}
|
||||
}
|
||||
|
||||
/* 頂点数を取得 */
|
||||
fun size(): Int {
|
||||
return adjList.size
|
||||
}
|
||||
|
||||
/* 辺を追加 */
|
||||
fun addEdge(vet1: Vertex, vet2: Vertex) {
|
||||
if (!adjList.containsKey(vet1) || !adjList.containsKey(vet2) || vet1 == vet2)
|
||||
throw IllegalArgumentException()
|
||||
// 辺 vet1 - vet2 を追加
|
||||
adjList[vet1]?.add(vet2)
|
||||
adjList[vet2]?.add(vet1)
|
||||
}
|
||||
|
||||
/* 辺を削除 */
|
||||
fun removeEdge(vet1: Vertex, vet2: Vertex) {
|
||||
if (!adjList.containsKey(vet1) || !adjList.containsKey(vet2) || vet1 == vet2)
|
||||
throw IllegalArgumentException()
|
||||
// 辺 vet1 - vet2 を削除
|
||||
adjList[vet1]?.remove(vet2)
|
||||
adjList[vet2]?.remove(vet1)
|
||||
}
|
||||
|
||||
/* 頂点を追加 */
|
||||
fun addVertex(vet: Vertex) {
|
||||
if (adjList.containsKey(vet))
|
||||
return
|
||||
// 隣接リストに新しいリストを追加
|
||||
adjList[vet] = mutableListOf()
|
||||
}
|
||||
|
||||
/* 頂点を削除 */
|
||||
fun removeVertex(vet: Vertex) {
|
||||
if (!adjList.containsKey(vet))
|
||||
throw IllegalArgumentException()
|
||||
// 隣接リストから頂点 vet に対応するリストを削除
|
||||
adjList.remove(vet)
|
||||
// 他の頂点のリストを走査し、vet を含むすべての辺を削除
|
||||
for (list in adjList.values) {
|
||||
list.remove(vet)
|
||||
}
|
||||
}
|
||||
|
||||
/* 隣接リストを出力 */
|
||||
fun print() {
|
||||
println("隣接リスト =")
|
||||
for (pair in adjList.entries) {
|
||||
val tmp = mutableListOf<Int>()
|
||||
for (vertex in pair.value) {
|
||||
tmp.add(vertex._val)
|
||||
}
|
||||
println("${pair.key._val}: $tmp,")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Driver Code */
|
||||
fun main() {
|
||||
/* 無向グラフを初期化 */
|
||||
val v = Vertex.valsToVets(intArrayOf(1, 3, 2, 5, 4))
|
||||
val edges = arrayOf(
|
||||
arrayOf(v[0], v[1]),
|
||||
arrayOf(v[0], v[3]),
|
||||
arrayOf(v[1], v[2]),
|
||||
arrayOf(v[2], v[3]),
|
||||
arrayOf(v[2], v[4]),
|
||||
arrayOf(v[3], v[4])
|
||||
)
|
||||
val graph = GraphAdjList(edges)
|
||||
println("\n初期化後のグラフは")
|
||||
graph.print()
|
||||
|
||||
/* 辺を追加 */
|
||||
// 頂点 1, 2 は v[0], v[2]
|
||||
graph.addEdge(v[0]!!, v[2]!!)
|
||||
println("\n辺 1-2 を追加した後のグラフは")
|
||||
graph.print()
|
||||
|
||||
/* 辺を削除 */
|
||||
// 頂点 1, 3 は v[0], v[1]
|
||||
graph.removeEdge(v[0]!!, v[1]!!)
|
||||
println("\n辺 1-3 を削除した後のグラフは")
|
||||
graph.print()
|
||||
|
||||
/* 頂点を追加 */
|
||||
val v5 = Vertex(6)
|
||||
graph.addVertex(v5)
|
||||
println("\n頂点 6 を追加した後のグラフは")
|
||||
graph.print()
|
||||
|
||||
/* 頂点を削除 */
|
||||
// 頂点 3 は v[1]
|
||||
graph.removeVertex(v[1]!!)
|
||||
println("\n頂点 3 を削除した後のグラフは")
|
||||
graph.print()
|
||||
}
|
||||
@@ -0,0 +1,134 @@
|
||||
/**
|
||||
* File: graph_adjacency_matrix.kt
|
||||
* Created Time: 2024-01-25
|
||||
* Author: curtishd (1023632660@qq.com)
|
||||
*/
|
||||
|
||||
package chapter_graph
|
||||
|
||||
import utils.printMatrix
|
||||
|
||||
/* 隣接行列に基づく無向グラフクラス */
|
||||
class GraphAdjMat(vertices: IntArray, edges: Array<IntArray>) {
|
||||
val vertices = mutableListOf<Int>() // 頂点リスト。要素は「頂点値」、インデックスは「頂点インデックス」を表す
|
||||
val adjMat = mutableListOf<MutableList<Int>>() // 隣接行列。行・列のインデックスは「頂点インデックス」に対応
|
||||
|
||||
/* コンストラクタ */
|
||||
init {
|
||||
// 頂点を追加
|
||||
for (vertex in vertices) {
|
||||
addVertex(vertex)
|
||||
}
|
||||
// 辺を追加
|
||||
// 注意:edges の各要素は頂点インデックスを表し、vertices の要素インデックスに対応する
|
||||
for (edge in edges) {
|
||||
addEdge(edge[0], edge[1])
|
||||
}
|
||||
}
|
||||
|
||||
/* 頂点数を取得 */
|
||||
fun size(): Int {
|
||||
return vertices.size
|
||||
}
|
||||
|
||||
/* 頂点を追加 */
|
||||
fun addVertex(_val: Int) {
|
||||
val n = size()
|
||||
// 頂点リストに新しい頂点の値を追加
|
||||
vertices.add(_val)
|
||||
// 隣接行列に 1 行追加
|
||||
val newRow = mutableListOf<Int>()
|
||||
for (j in 0..<n) {
|
||||
newRow.add(0)
|
||||
}
|
||||
adjMat.add(newRow)
|
||||
// 隣接行列に 1 列追加
|
||||
for (row in adjMat) {
|
||||
row.add(0)
|
||||
}
|
||||
}
|
||||
|
||||
/* 頂点を削除 */
|
||||
fun removeVertex(index: Int) {
|
||||
if (index >= size())
|
||||
throw IndexOutOfBoundsException()
|
||||
// 頂点リストから index の頂点を削除する
|
||||
vertices.removeAt(index)
|
||||
// 隣接行列で index 行を削除する
|
||||
adjMat.removeAt(index)
|
||||
// 隣接行列で index 列を削除する
|
||||
for (row in adjMat) {
|
||||
row.removeAt(index)
|
||||
}
|
||||
}
|
||||
|
||||
/* 辺を追加 */
|
||||
// 引数 i, j は vertices の要素インデックスに対応する
|
||||
fun addEdge(i: Int, j: Int) {
|
||||
// インデックスの範囲外と等値の処理
|
||||
if (i < 0 || j < 0 || i >= size() || j >= size() || i == j)
|
||||
throw IndexOutOfBoundsException()
|
||||
// 無向グラフでは、隣接行列は主対角線に関して対称、すなわち (i, j) == (j, i) を満たす
|
||||
adjMat[i][j] = 1
|
||||
adjMat[j][i] = 1
|
||||
}
|
||||
|
||||
/* 辺を削除 */
|
||||
// 引数 i, j は vertices の要素インデックスに対応する
|
||||
fun removeEdge(i: Int, j: Int) {
|
||||
// インデックスの範囲外と等値の処理
|
||||
if (i < 0 || j < 0 || i >= size() || j >= size() || i == j)
|
||||
throw IndexOutOfBoundsException()
|
||||
adjMat[i][j] = 0
|
||||
adjMat[j][i] = 0
|
||||
}
|
||||
|
||||
/* 隣接行列を出力 */
|
||||
fun print() {
|
||||
print("頂点リスト = ")
|
||||
println(vertices)
|
||||
println("隣接行列 =")
|
||||
printMatrix(adjMat)
|
||||
}
|
||||
}
|
||||
|
||||
/* Driver Code */
|
||||
fun main() {
|
||||
/* 無向グラフを初期化 */
|
||||
// edges の要素は頂点インデックス、すなわち vertices の要素インデックスに対応する点に注意
|
||||
val vertices = intArrayOf(1, 3, 2, 5, 4)
|
||||
val edges = arrayOf(
|
||||
intArrayOf(0, 1),
|
||||
intArrayOf(0, 3),
|
||||
intArrayOf(1, 2),
|
||||
intArrayOf(2, 3),
|
||||
intArrayOf(2, 4),
|
||||
intArrayOf(3, 4)
|
||||
)
|
||||
val graph = GraphAdjMat(vertices, edges)
|
||||
println("\n初期化後のグラフは")
|
||||
graph.print()
|
||||
|
||||
/* 辺を追加 */
|
||||
// 頂点 1, 2 のインデックスはそれぞれ 0, 2
|
||||
graph.addEdge(0, 2)
|
||||
println("\n辺 1-2 を追加した後のグラフは")
|
||||
graph.print()
|
||||
|
||||
/* 辺を削除 */
|
||||
// 頂点 1, 3 のインデックスはそれぞれ 0, 1
|
||||
graph.removeEdge(0, 1)
|
||||
println("\n辺 1-3 を削除した後のグラフは")
|
||||
graph.print()
|
||||
|
||||
/* 頂点を追加 */
|
||||
graph.addVertex(6)
|
||||
println("\n頂点 6 を追加した後のグラフは")
|
||||
graph.print()
|
||||
|
||||
/* 頂点を削除 */
|
||||
// 頂点 3 のインデックスは 1
|
||||
graph.removeVertex(1)
|
||||
println("\n頂点 3 を削除した後のグラフは")
|
||||
graph.print()
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
/**
|
||||
* File: graph_bfs.kt
|
||||
* Created Time: 2024-01-25
|
||||
* Author: curtishd (1023632660@qq.com)
|
||||
*/
|
||||
|
||||
package chapter_graph
|
||||
|
||||
import utils.Vertex
|
||||
import java.util.*
|
||||
|
||||
/* 幅優先探索 */
|
||||
// グラフを隣接リストで表し、指定した頂点の隣接頂点をすべて取得できるようにする
|
||||
fun graphBFS(graph: GraphAdjList, startVet: Vertex): MutableList<Vertex?> {
|
||||
// 頂点の走査順序
|
||||
val res = mutableListOf<Vertex?>()
|
||||
// 訪問済み頂点を記録するためのハッシュ集合
|
||||
val visited = HashSet<Vertex>()
|
||||
visited.add(startVet)
|
||||
// BFS の実装にキューを用いる
|
||||
val que = LinkedList<Vertex>()
|
||||
que.offer(startVet)
|
||||
// 頂点 vet を起点に、すべての頂点を訪問し終えるまで繰り返す
|
||||
while (!que.isEmpty()) {
|
||||
val vet = que.poll() // 先頭の頂点をデキュー
|
||||
res.add(vet) // 訪問した頂点を記録
|
||||
// この頂点のすべての隣接頂点を走査
|
||||
for (adjVet in graph.adjList[vet]!!) {
|
||||
if (visited.contains(adjVet))
|
||||
continue // 訪問済みの頂点をスキップ
|
||||
que.offer(adjVet) // 未訪問の頂点のみをキューに追加
|
||||
visited.add(adjVet) // この頂点を訪問済みにする
|
||||
}
|
||||
}
|
||||
// 頂点の走査順を返す
|
||||
return res
|
||||
}
|
||||
|
||||
/* Driver Code */
|
||||
fun main() {
|
||||
/* 無向グラフを初期化 */
|
||||
val v = Vertex.valsToVets(intArrayOf(0, 1, 2, 3, 4, 5, 6, 7, 8, 9))
|
||||
val edges = arrayOf(
|
||||
arrayOf(v[0], v[1]),
|
||||
arrayOf(v[0], v[3]),
|
||||
arrayOf(v[1], v[2]),
|
||||
arrayOf(v[1], v[4]),
|
||||
arrayOf(v[2], v[5]),
|
||||
arrayOf(v[3], v[4]),
|
||||
arrayOf(v[3], v[6]),
|
||||
arrayOf(v[4], v[5]),
|
||||
arrayOf(v[4], v[7]),
|
||||
arrayOf(v[5], v[8]),
|
||||
arrayOf(v[6], v[7]),
|
||||
arrayOf(v[7], v[8])
|
||||
)
|
||||
val graph = GraphAdjList(edges)
|
||||
println("\n初期化後のグラフは")
|
||||
graph.print()
|
||||
|
||||
/* 幅優先探索 */
|
||||
val res = graphBFS(graph, v[0]!!)
|
||||
println("\n幅優先探索(BFS)の頂点順序は")
|
||||
println(Vertex.vetsToVals(res))
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
/**
|
||||
* File: graph_dfs.kt
|
||||
* Created Time: 2024-01-25
|
||||
* Author: curtishd (1023632660@qq.com)
|
||||
*/
|
||||
|
||||
package chapter_graph
|
||||
|
||||
import utils.Vertex
|
||||
|
||||
/* 深さ優先走査の補助関数 */
|
||||
fun dfs(
|
||||
graph: GraphAdjList,
|
||||
visited: MutableSet<Vertex?>,
|
||||
res: MutableList<Vertex?>,
|
||||
vet: Vertex?
|
||||
) {
|
||||
res.add(vet) // 訪問した頂点を記録
|
||||
visited.add(vet) // この頂点を訪問済みにする
|
||||
// この頂点のすべての隣接頂点を走査
|
||||
for (adjVet in graph.adjList[vet]!!) {
|
||||
if (visited.contains(adjVet))
|
||||
continue // 訪問済みの頂点をスキップ
|
||||
// 隣接頂点を再帰的に訪問
|
||||
dfs(graph, visited, res, adjVet)
|
||||
}
|
||||
}
|
||||
|
||||
/* 深さ優先探索 */
|
||||
// グラフを隣接リストで表し、指定した頂点の隣接頂点をすべて取得できるようにする
|
||||
fun graphDFS(graph: GraphAdjList, startVet: Vertex?): MutableList<Vertex?> {
|
||||
// 頂点の走査順序
|
||||
val res = mutableListOf<Vertex?>()
|
||||
// 訪問済み頂点を記録するためのハッシュ集合
|
||||
val visited = HashSet<Vertex?>()
|
||||
dfs(graph, visited, res, startVet)
|
||||
return res
|
||||
}
|
||||
|
||||
/* Driver Code */
|
||||
fun main() {
|
||||
/* 無向グラフを初期化 */
|
||||
val v = Vertex.valsToVets(intArrayOf(0, 1, 2, 3, 4, 5, 6))
|
||||
val edges = arrayOf(
|
||||
arrayOf(v[0], v[1]),
|
||||
arrayOf(v[0], v[3]),
|
||||
arrayOf(v[1], v[2]),
|
||||
arrayOf(v[2], v[5]),
|
||||
arrayOf(v[4], v[5]),
|
||||
arrayOf(v[5], v[6])
|
||||
)
|
||||
val graph = GraphAdjList(edges)
|
||||
println("\n初期化後のグラフは")
|
||||
graph.print()
|
||||
|
||||
/* 深さ優先探索 */
|
||||
val res = graphDFS(graph, v[0])
|
||||
println("\n深さ優先探索(DFS)の頂点順序は")
|
||||
println(Vertex.vetsToVals(res))
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
/**
|
||||
* File: coin_change_greedy.kt
|
||||
* Created Time: 2024-01-25
|
||||
* Author: curtishd (1023632660@qq.com)
|
||||
*/
|
||||
|
||||
package chapter_greedy
|
||||
|
||||
/* コイン交換:貪欲法 */
|
||||
fun coinChangeGreedy(coins: IntArray, amt: Int): Int {
|
||||
// coins リストはソート済みと仮定する
|
||||
var am = amt
|
||||
var i = coins.size - 1
|
||||
var count = 0
|
||||
// 残額がなくなるまで貪欲選択を繰り返す
|
||||
while (am > 0) {
|
||||
// 残額以下で最も近い硬貨を見つける
|
||||
while (i > 0 && coins[i] > am) {
|
||||
i--
|
||||
}
|
||||
// coins[i] を選択する
|
||||
am -= coins[i]
|
||||
count++
|
||||
}
|
||||
// 実行可能な解が見つからなければ -1 を返す
|
||||
return if (am == 0) count else -1
|
||||
}
|
||||
|
||||
/* Driver Code */
|
||||
fun main() {
|
||||
// 貪欲法:大域最適解を保証できる
|
||||
var coins = intArrayOf(1, 5, 10, 20, 50, 100)
|
||||
var amt = 186
|
||||
var res = coinChangeGreedy(coins, amt)
|
||||
println("\ncoins = ${coins.contentToString()}, amt = $amt")
|
||||
println("$amt を作るのに必要な最小硬貨枚数は $res")
|
||||
|
||||
// 貪欲法:大域最適解を保証できない
|
||||
coins = intArrayOf(1, 20, 50)
|
||||
amt = 60
|
||||
res = coinChangeGreedy(coins, amt)
|
||||
println("\ncoins = ${coins.contentToString()}, amt = $amt")
|
||||
println("$amt を作るのに必要な最小硬貨枚数は $res")
|
||||
println("実際に必要な最小枚数は 3、つまり 20 + 20 + 20")
|
||||
|
||||
// 貪欲法:大域最適解を保証できない
|
||||
coins = intArrayOf(1, 49, 50)
|
||||
amt = 98
|
||||
res = coinChangeGreedy(coins, amt)
|
||||
println("\ncoins = ${coins.contentToString()}, amt = $amt")
|
||||
println("$amt を作るのに必要な最小硬貨枚数は $res")
|
||||
println("実際に必要な最小枚数は 2、つまり 49 + 49")
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
/**
|
||||
* File: fractional_knapsack.kt
|
||||
* Created Time: 2024-01-25
|
||||
* Author: curtishd (1023632660@qq.com)
|
||||
*/
|
||||
|
||||
package chapter_greedy
|
||||
|
||||
/* 品物 */
|
||||
class Item(
|
||||
val w: Int, // 品物
|
||||
val v: Int // 品物の価値
|
||||
)
|
||||
|
||||
/* 分数ナップサック:貪欲法 */
|
||||
fun fractionalKnapsack(wgt: IntArray, _val: IntArray, c: Int): Double {
|
||||
// 重さと価値の 2 属性を持つ品物リストを作成
|
||||
var cap = c
|
||||
val items = arrayOfNulls<Item>(wgt.size)
|
||||
for (i in wgt.indices) {
|
||||
items[i] = Item(wgt[i], _val[i])
|
||||
}
|
||||
// 単位価値 item.v / item.w の高い順にソートする
|
||||
items.sortBy { item: Item? -> -(item!!.v.toDouble() / item.w) }
|
||||
// 貪欲選択を繰り返す
|
||||
var res = 0.0
|
||||
for (item in items) {
|
||||
if (item!!.w <= cap) {
|
||||
// 残り容量が十分なら、現在の品物を丸ごとナップサックに入れる
|
||||
res += item.v
|
||||
cap -= item.w
|
||||
} else {
|
||||
// 残り容量が足りない場合は、現在の品物の一部だけをナップサックに入れる
|
||||
res += item.v.toDouble() / item.w * cap
|
||||
// 残り容量がないため、ループを抜ける
|
||||
break
|
||||
}
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
/* Driver Code */
|
||||
fun main() {
|
||||
val wgt = intArrayOf(10, 20, 30, 40, 50)
|
||||
val _val = intArrayOf(50, 120, 150, 210, 240)
|
||||
val cap = 50
|
||||
|
||||
// 貪欲法
|
||||
val res = fractionalKnapsack(wgt, _val, cap)
|
||||
println("ナップサック容量を超えない最大価値は $res")
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
/**
|
||||
* File: max_capacity.kt
|
||||
* Created Time: 2024-01-25
|
||||
* Author: curtishd (1023632660@qq.com)
|
||||
*/
|
||||
|
||||
package chapter_greedy
|
||||
|
||||
import kotlin.math.max
|
||||
import kotlin.math.min
|
||||
|
||||
/* 最大容量:貪欲法 */
|
||||
fun maxCapacity(ht: IntArray): Int {
|
||||
// i, j を初期化し、それぞれ配列の両端に置く
|
||||
var i = 0
|
||||
var j = ht.size - 1
|
||||
// 初期の最大容量は 0
|
||||
var res = 0
|
||||
// 2 枚の板が出会うまで貪欲選択を繰り返す
|
||||
while (i < j) {
|
||||
// 最大容量を更新する
|
||||
val cap = min(ht[i], ht[j]) * (j - i)
|
||||
res = max(res, cap)
|
||||
// 短い方を内側へ動かす
|
||||
if (ht[i] < ht[j]) {
|
||||
i++
|
||||
} else {
|
||||
j--
|
||||
}
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
/* Driver Code */
|
||||
fun main() {
|
||||
val ht = intArrayOf(3, 8, 5, 2, 7, 7, 3, 4)
|
||||
|
||||
// 貪欲法
|
||||
val res = maxCapacity(ht)
|
||||
println("最大容量は $res")
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
/**
|
||||
* File: max_product_cutting.kt
|
||||
* Created Time: 2024-01-25
|
||||
* Author: curtishd (1023632660@qq.com)
|
||||
*/
|
||||
|
||||
package chapter_greedy
|
||||
|
||||
import kotlin.math.pow
|
||||
|
||||
/* 最大切断積:貪欲法 */
|
||||
fun maxProductCutting(n: Int): Int {
|
||||
// n <= 3 のときは、必ず 1 を切り出す
|
||||
if (n <= 3) {
|
||||
return 1 * (n - 1)
|
||||
}
|
||||
// 貪欲に 3 を切り出し、a を 3 の個数、b を余りとする
|
||||
val a = n / 3
|
||||
val b = n % 3
|
||||
if (b == 1) {
|
||||
// 余りが 1 のときは、1 * 3 を 2 * 2 に変える
|
||||
return 3.0.pow((a - 1)).toInt() * 2 * 2
|
||||
}
|
||||
if (b == 2) {
|
||||
// 余りが 2 のときは、そのままにする
|
||||
return 3.0.pow(a).toInt() * 2 * 2
|
||||
}
|
||||
// 余りが 0 のときは、そのままにする
|
||||
return 3.0.pow(a).toInt()
|
||||
}
|
||||
|
||||
/* Driver Code */
|
||||
fun main() {
|
||||
val n = 58
|
||||
|
||||
// 貪欲法
|
||||
val res = maxProductCutting(n)
|
||||
println("最大分割積は $res")
|
||||
}
|
||||
@@ -0,0 +1,126 @@
|
||||
/**
|
||||
* File: array_hash_map.kt
|
||||
* Created Time: 2024-01-25
|
||||
* Author: curtishd (1023632660@qq.com)
|
||||
*/
|
||||
|
||||
package chapter_hashing
|
||||
|
||||
/* キーと値の組 */
|
||||
class Pair(
|
||||
var key: Int,
|
||||
var _val: String
|
||||
)
|
||||
|
||||
/* 配列ベースのハッシュテーブル */
|
||||
class ArrayHashMap {
|
||||
// 100 個のバケットを含む配列を初期化
|
||||
private val buckets = arrayOfNulls<Pair>(100)
|
||||
|
||||
/* ハッシュ関数 */
|
||||
fun hashFunc(key: Int): Int {
|
||||
val index = key % 100
|
||||
return index
|
||||
}
|
||||
|
||||
/* 検索操作 */
|
||||
fun get(key: Int): String? {
|
||||
val index = hashFunc(key)
|
||||
val pair = buckets[index] ?: return null
|
||||
return pair._val
|
||||
}
|
||||
|
||||
/* 追加操作 */
|
||||
fun put(key: Int, _val: String) {
|
||||
val pair = Pair(key, _val)
|
||||
val index = hashFunc(key)
|
||||
buckets[index] = pair
|
||||
}
|
||||
|
||||
/* 削除操作 */
|
||||
fun remove(key: Int) {
|
||||
val index = hashFunc(key)
|
||||
// null に設定し、削除を表す
|
||||
buckets[index] = null
|
||||
}
|
||||
|
||||
/* すべてのキーと値のペアを取得 */
|
||||
fun pairSet(): MutableList<Pair> {
|
||||
val pairSet = mutableListOf<Pair>()
|
||||
for (pair in buckets) {
|
||||
if (pair != null)
|
||||
pairSet.add(pair)
|
||||
}
|
||||
return pairSet
|
||||
}
|
||||
|
||||
/* すべてのキーを取得 */
|
||||
fun keySet(): MutableList<Int> {
|
||||
val keySet = mutableListOf<Int>()
|
||||
for (pair in buckets) {
|
||||
if (pair != null)
|
||||
keySet.add(pair.key)
|
||||
}
|
||||
return keySet
|
||||
}
|
||||
|
||||
/* すべての値を取得 */
|
||||
fun valueSet(): MutableList<String> {
|
||||
val valueSet = mutableListOf<String>()
|
||||
for (pair in buckets) {
|
||||
if (pair != null)
|
||||
valueSet.add(pair._val)
|
||||
}
|
||||
return valueSet
|
||||
}
|
||||
|
||||
/* ハッシュテーブルを出力 */
|
||||
fun print() {
|
||||
for (kv in pairSet()) {
|
||||
val key = kv.key
|
||||
val _val = kv._val
|
||||
println("$key -> $_val")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Driver Code */
|
||||
fun main() {
|
||||
/* ハッシュテーブルを初期化 */
|
||||
val map = ArrayHashMap()
|
||||
|
||||
/* 追加操作 */
|
||||
// ハッシュテーブルにキーと値のペア (key, value) を追加
|
||||
map.put(12836, "シャオハー")
|
||||
map.put(15937, "シャオルオ")
|
||||
map.put(16750, "シャオスワン")
|
||||
map.put(13276, "シャオファー")
|
||||
map.put(10583, "シャオヤ")
|
||||
println("\n追加完了後、ハッシュテーブルは\nKey -> Value")
|
||||
map.print()
|
||||
|
||||
/* 検索操作 */
|
||||
// キー key をハッシュテーブルに渡し、値 value を取得
|
||||
val name = map.get(15937)
|
||||
println("\n学籍番号 15937 を入力すると、名前 $name が見つかりました")
|
||||
|
||||
/* 削除操作 */
|
||||
// ハッシュテーブルからキーと値のペア (key, value) を削除
|
||||
map.remove(10583)
|
||||
println("\n10583 を削除した後、ハッシュテーブルは\nKey -> Value")
|
||||
map.print()
|
||||
|
||||
/* ハッシュテーブルを走査 */
|
||||
println("\nキーと値のペアを走査 Key -> Value")
|
||||
for (kv in map.pairSet()) {
|
||||
println("${kv.key} -> ${kv._val}")
|
||||
}
|
||||
println("\nキー Key を個別に走査")
|
||||
for (key in map.keySet()) {
|
||||
println(key)
|
||||
}
|
||||
println("\n値 Value を個別に走査")
|
||||
for (_val in map.valueSet()) {
|
||||
println(_val)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
/**
|
||||
* File: built_in_hash.kt
|
||||
* Created Time: 2024-01-25
|
||||
* Author: curtishd (1023632660@qq.com)
|
||||
*/
|
||||
|
||||
package chapter_hashing
|
||||
|
||||
import utils.ListNode
|
||||
|
||||
/* Driver Code */
|
||||
fun main() {
|
||||
val num = 3
|
||||
val hashNum = num.hashCode()
|
||||
println("整数 $num のハッシュ値は $hashNum")
|
||||
|
||||
val bol = true
|
||||
val hashBol = bol.hashCode()
|
||||
println("真偽値 $bol のハッシュ値は $hashBol")
|
||||
|
||||
val dec = 3.14159
|
||||
val hashDec = dec.hashCode()
|
||||
println("小数 $dec のハッシュ値は $hashDec")
|
||||
|
||||
val str = "Hello アルゴリズム"
|
||||
val hashStr = str.hashCode()
|
||||
println("文字列 $str のハッシュ値は $hashStr")
|
||||
|
||||
val arr = arrayOf<Any>(12836, "シャオハー")
|
||||
val hashTup = arr.contentHashCode()
|
||||
println("配列 ${arr.contentToString()} のハッシュ値は $hashTup")
|
||||
|
||||
val obj = ListNode(0)
|
||||
val hashObj = obj.hashCode()
|
||||
println("ノードオブジェクト $obj のハッシュ値は $hashObj")
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
/**
|
||||
* File: hash_map.kt
|
||||
* Created Time: 2024-01-25
|
||||
* Author: curtishd (1023632660@qq.com)
|
||||
*/
|
||||
|
||||
package chapter_hashing
|
||||
|
||||
import utils.printHashMap
|
||||
|
||||
/* Driver Code */
|
||||
fun main() {
|
||||
/* ハッシュテーブルを初期化 */
|
||||
val map = HashMap<Int, String>()
|
||||
|
||||
/* 追加操作 */
|
||||
// ハッシュテーブルにキーと値のペア (key, value) を追加
|
||||
map[12836] = "シャオハー"
|
||||
map[15937] = "シャオルオ"
|
||||
map[16750] = "シャオスワン"
|
||||
map[13276] = "シャオファー"
|
||||
map[10583] = "シャオヤ"
|
||||
println("\n追加完了後、ハッシュテーブルは\nKey -> Value")
|
||||
printHashMap(map)
|
||||
|
||||
/* 検索操作 */
|
||||
// キー key をハッシュテーブルに渡し、値 value を取得
|
||||
val name = map[15937]
|
||||
println("\n学籍番号 15937 を入力すると、名前 $name が見つかりました")
|
||||
|
||||
/* 削除操作 */
|
||||
// ハッシュテーブルからキーと値のペア (key, value) を削除
|
||||
map.remove(10583)
|
||||
println("\n10583 を削除した後、ハッシュテーブルは\nKey -> Value")
|
||||
printHashMap(map)
|
||||
|
||||
/* ハッシュテーブルを走査 */
|
||||
println("\nキーと値のペアを走査 Key->Value")
|
||||
for ((key, value) in map) {
|
||||
println("$key -> $value")
|
||||
}
|
||||
println("\nキー Key を個別に走査")
|
||||
for (key in map.keys) {
|
||||
println(key)
|
||||
}
|
||||
println("\n値 Value を個別に走査")
|
||||
for (_val in map.values) {
|
||||
println(_val)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,145 @@
|
||||
/**
|
||||
* File: hash_map_chaining.kt
|
||||
* Created Time: 2024-01-25
|
||||
* Author: curtishd (1023632660@qq.com)
|
||||
*/
|
||||
|
||||
package chapter_hashing
|
||||
|
||||
/* チェイン法ハッシュテーブル */
|
||||
class HashMapChaining {
|
||||
var size: Int // キーと値のペア数
|
||||
var capacity: Int // ハッシュテーブル容量
|
||||
val loadThres: Double // リサイズを発動する負荷率のしきい値
|
||||
val extendRatio: Int // 拡張倍率
|
||||
var buckets: MutableList<MutableList<Pair>> // バケット配列
|
||||
|
||||
/* コンストラクタ */
|
||||
init {
|
||||
size = 0
|
||||
capacity = 4
|
||||
loadThres = 2.0 / 3.0
|
||||
extendRatio = 2
|
||||
buckets = mutableListOf()
|
||||
for (i in 0..<capacity) {
|
||||
buckets.add(mutableListOf())
|
||||
}
|
||||
}
|
||||
|
||||
/* ハッシュ関数 */
|
||||
fun hashFunc(key: Int): Int {
|
||||
return key % capacity
|
||||
}
|
||||
|
||||
/* 負荷率 */
|
||||
fun loadFactor(): Double {
|
||||
return (size / capacity).toDouble()
|
||||
}
|
||||
|
||||
/* 検索操作 */
|
||||
fun get(key: Int): String? {
|
||||
val index = hashFunc(key)
|
||||
val bucket = buckets[index]
|
||||
// バケットを走査し、key が見つかれば対応する val を返す
|
||||
for (pair in bucket) {
|
||||
if (pair.key == key) return pair._val
|
||||
}
|
||||
// key が見つからない場合は null を返す
|
||||
return null
|
||||
}
|
||||
|
||||
/* 追加操作 */
|
||||
fun put(key: Int, _val: String) {
|
||||
// 負荷率がしきい値を超えたら、リサイズを実行
|
||||
if (loadFactor() > loadThres) {
|
||||
extend()
|
||||
}
|
||||
val index = hashFunc(key)
|
||||
val bucket = buckets[index]
|
||||
// バケットを走査し、指定した key が見つかれば対応する val を更新して返す
|
||||
for (pair in bucket) {
|
||||
if (pair.key == key) {
|
||||
pair._val = _val
|
||||
return
|
||||
}
|
||||
}
|
||||
// その key が存在しなければ、キーと値のペアを末尾に追加
|
||||
val pair = Pair(key, _val)
|
||||
bucket.add(pair)
|
||||
size++
|
||||
}
|
||||
|
||||
/* 削除操作 */
|
||||
fun remove(key: Int) {
|
||||
val index = hashFunc(key)
|
||||
val bucket = buckets[index]
|
||||
// バケットを走査してキーと値のペアを削除
|
||||
for (pair in bucket) {
|
||||
if (pair.key == key) {
|
||||
bucket.remove(pair)
|
||||
size--
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* ハッシュテーブルを拡張 */
|
||||
fun extend() {
|
||||
// 元のハッシュテーブルを一時保存
|
||||
val bucketsTmp = buckets
|
||||
// リサイズ後の新しいハッシュテーブルを初期化
|
||||
capacity *= extendRatio
|
||||
// mutablelist には固定サイズがない
|
||||
buckets = mutableListOf()
|
||||
for (i in 0..<capacity) {
|
||||
buckets.add(mutableListOf())
|
||||
}
|
||||
size = 0
|
||||
// キーと値のペアを元のハッシュテーブルから新しいハッシュテーブルへ移す
|
||||
for (bucket in bucketsTmp) {
|
||||
for (pair in bucket) {
|
||||
put(pair.key, pair._val)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* ハッシュテーブルを出力 */
|
||||
fun print() {
|
||||
for (bucket in buckets) {
|
||||
val res = mutableListOf<String>()
|
||||
for (pair in bucket) {
|
||||
val k = pair.key
|
||||
val v = pair._val
|
||||
res.add("$k -> $v")
|
||||
}
|
||||
println(res)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Driver Code */
|
||||
fun main() {
|
||||
/* ハッシュテーブルを初期化 */
|
||||
val map = HashMapChaining()
|
||||
|
||||
/* 追加操作 */
|
||||
// ハッシュテーブルにキーと値のペア (key, value) を追加
|
||||
map.put(12836, "シャオハー")
|
||||
map.put(15937, "シャオルオ")
|
||||
map.put(16750, "シャオスワン")
|
||||
map.put(13276, "シャオファー")
|
||||
map.put(10583, "シャオヤ")
|
||||
println("\n追加完了後、ハッシュテーブルは\nKey -> Value")
|
||||
map.print()
|
||||
|
||||
/* 検索操作 */
|
||||
// キー key をハッシュテーブルに渡し、値 value を取得
|
||||
val name = map.get(13276)
|
||||
println("\n学籍番号 13276 を入力すると、名前 $name が見つかりました")
|
||||
|
||||
/* 削除操作 */
|
||||
// ハッシュテーブルからキーと値のペア (key, value) を削除
|
||||
map.remove(12836)
|
||||
println("\n12836 を削除した後、ハッシュテーブルは\nKey -> Value")
|
||||
map.print()
|
||||
}
|
||||
@@ -0,0 +1,161 @@
|
||||
/**
|
||||
* File: hash_map_open_addressing.kt
|
||||
* Created Time: 2024-01-25
|
||||
* Author: curtishd (1023632660@qq.com)
|
||||
*/
|
||||
|
||||
package chapter_hashing
|
||||
|
||||
/* オープンアドレス法ハッシュテーブル */
|
||||
class HashMapOpenAddressing {
|
||||
private var size: Int // キーと値のペア数
|
||||
private var capacity: Int // ハッシュテーブル容量
|
||||
private val loadThres: Double // リサイズを発動する負荷率のしきい値
|
||||
private val extendRatio: Int // 拡張倍率
|
||||
private var buckets: Array<Pair?> // バケット配列
|
||||
private val TOMBSTONE: Pair // 削除済みマーク
|
||||
|
||||
/* コンストラクタ */
|
||||
init {
|
||||
size = 0
|
||||
capacity = 4
|
||||
loadThres = 2.0 / 3.0
|
||||
extendRatio = 2
|
||||
buckets = arrayOfNulls(capacity)
|
||||
TOMBSTONE = Pair(-1, "-1")
|
||||
}
|
||||
|
||||
/* ハッシュ関数 */
|
||||
fun hashFunc(key: Int): Int {
|
||||
return key % capacity
|
||||
}
|
||||
|
||||
/* 負荷率 */
|
||||
fun loadFactor(): Double {
|
||||
return (size / capacity).toDouble()
|
||||
}
|
||||
|
||||
/* key に対応するバケットインデックスを探す */
|
||||
fun findBucket(key: Int): Int {
|
||||
var index = hashFunc(key)
|
||||
var firstTombstone = -1
|
||||
// 線形プロービングを行い、空バケットに達したら終了
|
||||
while (buckets[index] != null) {
|
||||
// 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 if (firstTombstone == -1) index else firstTombstone
|
||||
}
|
||||
|
||||
/* 検索操作 */
|
||||
fun get(key: Int): String? {
|
||||
// key に対応するバケットインデックスを探す
|
||||
val index = findBucket(key)
|
||||
// キーと値の組が見つかったら、対応する val を返す
|
||||
if (buckets[index] != null && buckets[index] != TOMBSTONE) {
|
||||
return buckets[index]?._val
|
||||
}
|
||||
// キーと値の組が存在しなければ null を返す
|
||||
return null
|
||||
}
|
||||
|
||||
/* 追加操作 */
|
||||
fun put(key: Int, _val: String) {
|
||||
// 負荷率がしきい値を超えたら、リサイズを実行
|
||||
if (loadFactor() > loadThres) {
|
||||
extend()
|
||||
}
|
||||
// key に対応するバケットインデックスを探す
|
||||
val index = findBucket(key)
|
||||
// キーと値の組が見つかったら、val を上書きして返す
|
||||
if (buckets[index] != null && buckets[index] != TOMBSTONE) {
|
||||
buckets[index]!!._val = _val
|
||||
return
|
||||
}
|
||||
// キーと値の組が存在しない場合は、その組を追加する
|
||||
buckets[index] = Pair(key, _val)
|
||||
size++
|
||||
}
|
||||
|
||||
/* 削除操作 */
|
||||
fun remove(key: Int) {
|
||||
// key に対応するバケットインデックスを探す
|
||||
val index = findBucket(key)
|
||||
// キーと値の組が見つかったら、削除マーカーで上書きする
|
||||
if (buckets[index] != null && buckets[index] != TOMBSTONE) {
|
||||
buckets[index] = TOMBSTONE
|
||||
size--
|
||||
}
|
||||
}
|
||||
|
||||
/* ハッシュテーブルを拡張 */
|
||||
fun extend() {
|
||||
// 元のハッシュテーブルを一時保存
|
||||
val bucketsTmp = buckets
|
||||
// リサイズ後の新しいハッシュテーブルを初期化
|
||||
capacity *= extendRatio
|
||||
buckets = arrayOfNulls(capacity)
|
||||
size = 0
|
||||
// キーと値のペアを元のハッシュテーブルから新しいハッシュテーブルへ移す
|
||||
for (pair in bucketsTmp) {
|
||||
if (pair != null && pair != TOMBSTONE) {
|
||||
put(pair.key, pair._val)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* ハッシュテーブルを出力 */
|
||||
fun print() {
|
||||
for (pair in buckets) {
|
||||
if (pair == null) {
|
||||
println("null")
|
||||
} else if (pair == TOMBSTONE) {
|
||||
println("TOMESTOME")
|
||||
} else {
|
||||
println("${pair.key} -> ${pair._val}")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Driver Code */
|
||||
fun main() {
|
||||
// ハッシュテーブルを初期化
|
||||
val hashmap = HashMapOpenAddressing()
|
||||
|
||||
// 追加操作
|
||||
// ハッシュテーブルにキーと値の組 (key, val) を追加する
|
||||
hashmap.put(12836, "シャオハー")
|
||||
hashmap.put(15937, "シャオルオ")
|
||||
hashmap.put(16750, "シャオスワン")
|
||||
hashmap.put(13276, "シャオファー")
|
||||
hashmap.put(10583, "シャオヤー")
|
||||
println("\n追加完了後、ハッシュテーブルは\nKey -> Value")
|
||||
hashmap.print()
|
||||
|
||||
// 検索操作
|
||||
// ハッシュテーブルにキー key を入力し、値 val を得る
|
||||
val name = hashmap.get(13276)
|
||||
println("\n学籍番号 13276 を入力すると、名前 $name が見つかりました")
|
||||
|
||||
// 削除操作
|
||||
// ハッシュテーブルからキーと値の組 (key, val) を削除する
|
||||
hashmap.remove(16750)
|
||||
println("\n16750 を削除した後、ハッシュテーブルは\nKey -> Value")
|
||||
hashmap.print()
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
/**
|
||||
* File: simple_hash.kt
|
||||
* Created Time: 2024-01-25
|
||||
* Author: curtishd (1023632660@qq.com)
|
||||
*/
|
||||
|
||||
package chapter_hashing
|
||||
|
||||
/* 加算ハッシュ */
|
||||
fun addHash(key: String): Int {
|
||||
var hash = 0L
|
||||
val MODULUS = 1000000007
|
||||
for (c in key.toCharArray()) {
|
||||
hash = (hash + c.code) % MODULUS
|
||||
}
|
||||
return hash.toInt()
|
||||
}
|
||||
|
||||
/* 乗算ハッシュ */
|
||||
fun mulHash(key: String): Int {
|
||||
var hash = 0L
|
||||
val MODULUS = 1000000007
|
||||
for (c in key.toCharArray()) {
|
||||
hash = (31 * hash + c.code) % MODULUS
|
||||
}
|
||||
return hash.toInt()
|
||||
}
|
||||
|
||||
/* XOR ハッシュ */
|
||||
fun xorHash(key: String): Int {
|
||||
var hash = 0
|
||||
val MODULUS = 1000000007
|
||||
for (c in key.toCharArray()) {
|
||||
hash = hash xor c.code
|
||||
}
|
||||
return hash and MODULUS
|
||||
}
|
||||
|
||||
/* 回転ハッシュ */
|
||||
fun rotHash(key: String): Int {
|
||||
var hash = 0L
|
||||
val MODULUS = 1000000007
|
||||
for (c in key.toCharArray()) {
|
||||
hash = ((hash shl 4) xor (hash shr 28) xor c.code.toLong()) % MODULUS
|
||||
}
|
||||
return hash.toInt()
|
||||
}
|
||||
|
||||
/* Driver Code */
|
||||
fun main() {
|
||||
val key = "Hello アルゴリズム"
|
||||
|
||||
var hash = addHash(key)
|
||||
println("加算ハッシュ値は $hash")
|
||||
|
||||
hash = mulHash(key)
|
||||
println("乗算ハッシュ値は $hash")
|
||||
|
||||
hash = xorHash(key)
|
||||
println("XORハッシュ値は $hash")
|
||||
|
||||
hash = rotHash(key)
|
||||
println("回転ハッシュ値は $hash")
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
/**
|
||||
* File: heap.kt
|
||||
* Created Time: 2024-01-25
|
||||
* Author: curtishd (1023632660@qq.com)
|
||||
*/
|
||||
|
||||
package chapter_heap
|
||||
|
||||
import utils.printHeap
|
||||
import java.util.*
|
||||
|
||||
fun testPush(heap: Queue<Int>, _val: Int) {
|
||||
heap.offer(_val) // 要素をヒープに追加
|
||||
print("\n要素 $_val をヒープに追加した後\n")
|
||||
printHeap(heap)
|
||||
}
|
||||
|
||||
fun testPop(heap: Queue<Int>) {
|
||||
val _val = heap.poll() // ヒープ頂点の要素を取り出す
|
||||
print("\nヒープ先頭要素 $_val を取り出した後\n")
|
||||
printHeap(heap)
|
||||
}
|
||||
|
||||
/* Driver Code */
|
||||
fun main() {
|
||||
/* ヒープを初期化 */
|
||||
// 最小ヒープを初期化
|
||||
var minHeap = PriorityQueue<Int>()
|
||||
|
||||
// 最大ヒープを初期化する(lambda 式で Comparator を変更すればよい)
|
||||
val maxHeap = PriorityQueue { a: Int, b: Int -> b - a }
|
||||
|
||||
println("\n以下のテストケースは最大ヒープです")
|
||||
|
||||
/* 要素をヒープに追加 */
|
||||
testPush(maxHeap, 1)
|
||||
testPush(maxHeap, 3)
|
||||
testPush(maxHeap, 2)
|
||||
testPush(maxHeap, 5)
|
||||
testPush(maxHeap, 4)
|
||||
|
||||
/* ヒープ頂点の要素を取得 */
|
||||
val peek = maxHeap.peek()
|
||||
print("\nヒープ先頭要素は $peek\n")
|
||||
|
||||
/* ヒープ頂点の要素を取り出す */
|
||||
testPop(maxHeap)
|
||||
testPop(maxHeap)
|
||||
testPop(maxHeap)
|
||||
testPop(maxHeap)
|
||||
testPop(maxHeap)
|
||||
|
||||
/* ヒープのサイズを取得 */
|
||||
val size = maxHeap.size
|
||||
print("\nヒープ要素数は $size\n")
|
||||
|
||||
/* ヒープが空かどうかを判定 */
|
||||
val isEmpty = maxHeap.isEmpty()
|
||||
print("\nヒープが空かどうか $isEmpty\n")
|
||||
|
||||
/* リストを入力してヒープを構築 */
|
||||
// 時間計算量は O(n) であり、O(nlogn) ではない
|
||||
minHeap = PriorityQueue(mutableListOf<Int?>(1, 3, 2, 5, 4))
|
||||
println("\n入力リストから最小ヒープを構築した後")
|
||||
printHeap(minHeap)
|
||||
}
|
||||
@@ -0,0 +1,160 @@
|
||||
/**
|
||||
* File: my_heap.kt
|
||||
* Created Time: 2024-01-25
|
||||
* Author: curtishd (1023632660@qq.com)
|
||||
*/
|
||||
|
||||
package chapter_heap
|
||||
|
||||
import utils.printHeap
|
||||
import java.util.*
|
||||
|
||||
/* 最大ヒープ */
|
||||
class MaxHeap(nums: MutableList<Int>?) {
|
||||
// 配列ではなくリストを使うことで、拡張を考慮する必要がない
|
||||
private val maxHeap = mutableListOf<Int>()
|
||||
|
||||
/* コンストラクタ。入力リストに基づいてヒープを構築する */
|
||||
init {
|
||||
// リスト要素をそのままヒープに追加
|
||||
maxHeap.addAll(nums!!)
|
||||
// 葉ノード以外のすべてのノードをヒープ化
|
||||
for (i in parent(size() - 1) downTo 0) {
|
||||
siftDown(i)
|
||||
}
|
||||
}
|
||||
|
||||
/* 左子ノードのインデックスを取得 */
|
||||
private fun left(i: Int): Int {
|
||||
return 2 * i + 1
|
||||
}
|
||||
|
||||
/* 右子ノードのインデックスを取得 */
|
||||
private fun right(i: Int): Int {
|
||||
return 2 * i + 2
|
||||
}
|
||||
|
||||
/* 親ノードのインデックスを取得 */
|
||||
private fun parent(i: Int): Int {
|
||||
return (i - 1) / 2 // 切り捨て除算
|
||||
}
|
||||
|
||||
/* 要素を交換 */
|
||||
private fun swap(i: Int, j: Int) {
|
||||
val temp = maxHeap[i]
|
||||
maxHeap[i] = maxHeap[j]
|
||||
maxHeap[j] = temp
|
||||
}
|
||||
|
||||
/* ヒープのサイズを取得 */
|
||||
fun size(): Int {
|
||||
return maxHeap.size
|
||||
}
|
||||
|
||||
/* ヒープが空かどうかを判定 */
|
||||
fun isEmpty(): Boolean {
|
||||
/* ヒープが空かどうかを判定 */
|
||||
return size() == 0
|
||||
}
|
||||
|
||||
/* ヒープ先頭要素にアクセス */
|
||||
fun peek(): Int {
|
||||
return maxHeap[0]
|
||||
}
|
||||
|
||||
/* 要素をヒープに追加 */
|
||||
fun push(_val: Int) {
|
||||
// ノードを追加
|
||||
maxHeap.add(_val)
|
||||
// 下から上へヒープ化
|
||||
siftUp(size() - 1)
|
||||
}
|
||||
|
||||
/* ノード i から始めて、下から上へヒープ化 */
|
||||
private fun siftUp(it: Int) {
|
||||
// Kotlin の関数引数は不変のため、一時変数を作成する
|
||||
var i = it
|
||||
while (true) {
|
||||
// ノード i の親ノードを取得
|
||||
val p = parent(i)
|
||||
// 「根ノードを越えた」または「ノードの修復が不要」になったらヒープ化を終了
|
||||
if (p < 0 || maxHeap[i] <= maxHeap[p]) break
|
||||
// 2 つのノードを交換
|
||||
swap(i, p)
|
||||
// ループで下から上へヒープ化
|
||||
i = p
|
||||
}
|
||||
}
|
||||
|
||||
/* 要素をヒープから取り出す */
|
||||
fun pop(): Int {
|
||||
// 空判定の処理
|
||||
if (isEmpty()) throw IndexOutOfBoundsException()
|
||||
// 根ノードと最も右の葉ノードを交換(先頭要素と末尾要素を交換)
|
||||
swap(0, size() - 1)
|
||||
// ノードを削除
|
||||
val _val = maxHeap.removeAt(size() - 1)
|
||||
// 上から下へヒープ化
|
||||
siftDown(0)
|
||||
// ヒープ先頭要素を返す
|
||||
return _val
|
||||
}
|
||||
|
||||
/* ノード i から始めて、上から下へヒープ化 */
|
||||
private fun siftDown(it: Int) {
|
||||
// Kotlin の関数引数は不変のため、一時変数を作成する
|
||||
var i = it
|
||||
while (true) {
|
||||
// ノード i, l, r のうち値が最大のノードを ma とする
|
||||
val l = left(i)
|
||||
val r = right(i)
|
||||
var ma = i
|
||||
if (l < size() && maxHeap[l] > maxHeap[ma]) ma = l
|
||||
if (r < size() && maxHeap[r] > maxHeap[ma]) ma = r
|
||||
// ノード i が最大、またはインデックス l, r が範囲外なら、ヒープ化は不要なので抜ける
|
||||
if (ma == i) break
|
||||
// 2 つのノードを交換
|
||||
swap(i, ma)
|
||||
// ループで上から下へヒープ化
|
||||
i = ma
|
||||
}
|
||||
}
|
||||
|
||||
/* ヒープ(二分木)を出力 */
|
||||
fun print() {
|
||||
val queue = PriorityQueue { a: Int, b: Int -> b - a }
|
||||
queue.addAll(maxHeap)
|
||||
printHeap(queue)
|
||||
}
|
||||
}
|
||||
|
||||
/* Driver Code */
|
||||
fun main() {
|
||||
/* 最大ヒープを初期化 */
|
||||
val maxHeap = MaxHeap(mutableListOf(9, 8, 6, 6, 7, 5, 2, 1, 4, 3, 6, 2))
|
||||
println("\n入力リストからヒープを構築した後")
|
||||
maxHeap.print()
|
||||
|
||||
/* ヒープ頂点の要素を取得 */
|
||||
var peek = maxHeap.peek()
|
||||
print("\nヒープ先頭要素は $peek\n")
|
||||
|
||||
/* 要素をヒープに追加 */
|
||||
val _val = 7
|
||||
maxHeap.push(_val)
|
||||
print("\n要素 $_val をヒープに追加した後\n")
|
||||
maxHeap.print()
|
||||
|
||||
/* ヒープ頂点の要素を取り出す */
|
||||
peek = maxHeap.pop()
|
||||
print("\nヒープ先頭要素 $peek を取り出した後\n")
|
||||
maxHeap.print()
|
||||
|
||||
/* ヒープのサイズを取得 */
|
||||
val size = maxHeap.size()
|
||||
print("\nヒープ要素数は $size\n")
|
||||
|
||||
/* ヒープが空かどうかを判定 */
|
||||
val isEmpty = maxHeap.isEmpty()
|
||||
print("\nヒープが空かどうか $isEmpty\n")
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
/**
|
||||
* File: top_k.kt
|
||||
* Created Time: 2024-01-25
|
||||
* Author: curtishd (1023632660@qq.com)
|
||||
*/
|
||||
|
||||
package chapter_heap
|
||||
|
||||
import utils.printHeap
|
||||
import java.util.*
|
||||
|
||||
/* ヒープに基づいて配列中の最大の k 個の要素を探す */
|
||||
fun topKHeap(nums: IntArray, k: Int): Queue<Int> {
|
||||
// 最小ヒープを初期化
|
||||
val heap = PriorityQueue<Int>()
|
||||
// 配列の先頭 k 個の要素をヒープに追加
|
||||
for (i in 0..<k) {
|
||||
heap.offer(nums[i])
|
||||
}
|
||||
// k+1 番目の要素から開始し、ヒープ長を k に保つ
|
||||
for (i in k..<nums.size) {
|
||||
// 現在の要素がヒープ先頭より大きければ、ヒープ先頭を取り出して現在の要素を追加する
|
||||
if (nums[i] > heap.peek()) {
|
||||
heap.poll()
|
||||
heap.offer(nums[i])
|
||||
}
|
||||
}
|
||||
return heap
|
||||
}
|
||||
|
||||
/* Driver Code */
|
||||
fun main() {
|
||||
val nums = intArrayOf(1, 7, 6, 3, 2)
|
||||
val k = 3
|
||||
val res = topKHeap(nums, k)
|
||||
println("最大の $k 個の要素は")
|
||||
printHeap(res)
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
/**
|
||||
* File: binary_search.kt
|
||||
* Created Time: 2024-01-25
|
||||
* Author: curtishd (1023632660@qq.com)
|
||||
*/
|
||||
|
||||
package chapter_searching
|
||||
|
||||
/* 二分探索(両閉区間) */
|
||||
fun binarySearch(nums: IntArray, target: Int): Int {
|
||||
// 両閉区間 [0, n-1] を初期化する。つまり i, j はそれぞれ配列の先頭要素と末尾要素を指す
|
||||
var i = 0
|
||||
var j = nums.size - 1
|
||||
// ループし、探索区間が空になったら終了する(i > j で空)
|
||||
while (i <= j) {
|
||||
val 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
|
||||
}
|
||||
|
||||
/* 二分探索(左閉右開区間) */
|
||||
fun binarySearchLCRO(nums: IntArray, target: Int): Int {
|
||||
// 左閉右開区間 [0, n) を初期化する。つまり i, j はそれぞれ配列の先頭要素と末尾要素+1を指す
|
||||
var i = 0
|
||||
var j = nums.size
|
||||
// ループし、探索区間が空になったら終了する(i = j で空)
|
||||
while (i < j) {
|
||||
val 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
|
||||
}
|
||||
|
||||
/* Driver Code */
|
||||
fun main() {
|
||||
val target = 6
|
||||
val nums = intArrayOf(1, 3, 6, 8, 12, 15, 23, 26, 31, 35)
|
||||
|
||||
/* 二分探索(両閉区間) */
|
||||
var index = binarySearch(nums, target)
|
||||
println("対象要素 6 のインデックス = $index")
|
||||
|
||||
/* 二分探索(左閉右開区間) */
|
||||
index = binarySearchLCRO(nums, target)
|
||||
println("対象要素 6 のインデックス = $index")
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
/**
|
||||
* File: binary_search_edge.kt
|
||||
* Created Time: 2024-01-25
|
||||
* Author: curtishd (1023632660@qq.com)
|
||||
*/
|
||||
|
||||
package chapter_searching
|
||||
|
||||
/* 最も左の target を二分探索 */
|
||||
fun binarySearchLeftEdge(nums: IntArray, target: Int): Int {
|
||||
// target の挿入位置を探すのと等価
|
||||
val i = binarySearchInsertion(nums, target)
|
||||
// target が見つからなければ、-1 を返す
|
||||
if (i == nums.size || nums[i] != target) {
|
||||
return -1
|
||||
}
|
||||
// target が見つかったら、インデックス i を返す
|
||||
return i
|
||||
}
|
||||
|
||||
/* 最も右の target を二分探索 */
|
||||
fun binarySearchRightEdge(nums: IntArray, target: Int): Int {
|
||||
// 最左の target + 1 を探す問題に変換する
|
||||
val i = binarySearchInsertion(nums, target + 1)
|
||||
// j は最も右の target を指し、i は target より大きい最初の要素を指す
|
||||
val j = i - 1
|
||||
// target が見つからなければ、-1 を返す
|
||||
if (j == -1 || nums[j] != target) {
|
||||
return -1
|
||||
}
|
||||
// target が見つかったら、インデックス j を返す
|
||||
return j
|
||||
}
|
||||
|
||||
/* Driver Code */
|
||||
fun main() {
|
||||
// 重複要素を含む配列
|
||||
val nums = intArrayOf(1, 3, 6, 6, 6, 6, 6, 10, 12, 15)
|
||||
println("\n配列 nums = ${nums.contentToString()}")
|
||||
|
||||
// 二分探索で左端と右端を探す
|
||||
for (target in intArrayOf(6, 7)) {
|
||||
var index = binarySearchLeftEdge(nums, target)
|
||||
println("最も左にある要素 $target のインデックスは $index")
|
||||
index = binarySearchRightEdge(nums, target)
|
||||
println("最も右にある要素 $target のインデックスは $index")
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
/**
|
||||
* File: binary_search_insertion.kt
|
||||
* Created Time: 2024-01-25
|
||||
* Author: curtishd (1023632660@qq.com)
|
||||
*/
|
||||
|
||||
package chapter_searching
|
||||
|
||||
/* 二分探索で挿入位置を探す(重複要素なし) */
|
||||
fun binarySearchInsertionSimple(nums: IntArray, target: Int): Int {
|
||||
var i = 0
|
||||
var j = nums.size - 1 // 両閉区間 [0, n-1] を初期化
|
||||
while (i <= j) {
|
||||
val 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
|
||||
}
|
||||
|
||||
/* 二分探索で挿入位置を探す(重複要素あり) */
|
||||
fun binarySearchInsertion(nums: IntArray, target: Int): Int {
|
||||
var i = 0
|
||||
var j = nums.size - 1 // 両閉区間 [0, n-1] を初期化
|
||||
while (i <= j) {
|
||||
val 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
|
||||
}
|
||||
|
||||
/* Driver Code */
|
||||
fun main() {
|
||||
// 重複要素のない配列
|
||||
var nums = intArrayOf(1, 3, 6, 8, 12, 15, 23, 26, 31, 35)
|
||||
println("\n配列 nums = ${nums.contentToString()}")
|
||||
// 二分探索で挿入位置を探す
|
||||
for (target in intArrayOf(6, 9)) {
|
||||
val index = binarySearchInsertionSimple(nums, target)
|
||||
println("要素 $target の挿入位置のインデックスは $index")
|
||||
}
|
||||
|
||||
// 重複要素を含む配列
|
||||
nums = intArrayOf(1, 3, 6, 6, 6, 6, 6, 10, 12, 15)
|
||||
println("\n配列 nums = ${nums.contentToString()}")
|
||||
|
||||
// 二分探索で挿入位置を探す
|
||||
for (target in intArrayOf(2, 6, 20)) {
|
||||
val index = binarySearchInsertion(nums, target)
|
||||
println("要素 $target の挿入位置のインデックスは $index")
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
/**
|
||||
* File: hashing_search.kt
|
||||
* Created Time: 2024-01-25
|
||||
* Author: curtishd (1023632660@qq.com)
|
||||
*/
|
||||
|
||||
package chapter_searching
|
||||
|
||||
import utils.ListNode
|
||||
|
||||
/* ハッシュ探索(配列) */
|
||||
fun hashingSearchArray(map: Map<Int?, Int>, target: Int): Int {
|
||||
// ハッシュテーブルの key: 対象要素、_val: インデックス
|
||||
// ハッシュテーブルにその key がなければ -1 を返す
|
||||
return map.getOrDefault(target, -1)
|
||||
}
|
||||
|
||||
/* ハッシュ探索(連結リスト) */
|
||||
fun hashingSearchLinkedList(map: Map<Int?, ListNode?>, target: Int): ListNode? {
|
||||
// ハッシュテーブルの key: 対象ノードの値、_val: ノードオブジェクト
|
||||
// ハッシュテーブルにその key がなければ null を返す
|
||||
return map.getOrDefault(target, null)
|
||||
}
|
||||
|
||||
/* Driver Code */
|
||||
fun main() {
|
||||
val target = 3
|
||||
|
||||
/* ハッシュ探索(配列) */
|
||||
val nums = intArrayOf(1, 5, 3, 2, 4, 7, 5, 9, 10, 8)
|
||||
// ハッシュテーブルを初期化
|
||||
val map = HashMap<Int?, Int>()
|
||||
for (i in nums.indices) {
|
||||
map[nums[i]] = i // key: 要素、_val: インデックス
|
||||
}
|
||||
val index = hashingSearchArray(map, target)
|
||||
println("目標要素 3 のインデックス = $index")
|
||||
|
||||
/* ハッシュ探索(連結リスト) */
|
||||
var head = ListNode.arrToLinkedList(nums)
|
||||
// ハッシュテーブルを初期化
|
||||
val map1 = HashMap<Int?, ListNode?>()
|
||||
while (head != null) {
|
||||
map1[head._val] = head // key: ノード値、_val: ノード
|
||||
head = head.next
|
||||
}
|
||||
val node = hashingSearchLinkedList(map1, target)
|
||||
println("目標ノード値 3 に対応するノードオブジェクトは $node")
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
/**
|
||||
* File: linear_search.kt
|
||||
* Created Time: 2024-01-25
|
||||
* Author: curtishd (1023632660@qq.com)
|
||||
*/
|
||||
|
||||
package chapter_searching
|
||||
|
||||
import utils.ListNode
|
||||
|
||||
/* 線形探索(配列) */
|
||||
fun linearSearchArray(nums: IntArray, target: Int): Int {
|
||||
// 配列を走査
|
||||
for (i in nums.indices) {
|
||||
// 目標要素が見つかったらそのインデックスを返す
|
||||
if (nums[i] == target)
|
||||
return i
|
||||
}
|
||||
// 目標要素が見つからなければ -1 を返す
|
||||
return -1
|
||||
}
|
||||
|
||||
/* 線形探索(連結リスト) */
|
||||
fun linearSearchLinkedList(h: ListNode?, target: Int): ListNode? {
|
||||
// 連結リストを走査
|
||||
var head = h
|
||||
while (head != null) {
|
||||
// 対象ノードが見つかったら、それを返す
|
||||
if (head._val == target)
|
||||
return head
|
||||
head = head.next
|
||||
}
|
||||
// 対象ノードが見つからない場合は null を返す
|
||||
return null
|
||||
}
|
||||
|
||||
/* Driver Code */
|
||||
fun main() {
|
||||
val target = 3
|
||||
|
||||
/* 配列で線形探索を行う */
|
||||
val nums = intArrayOf(1, 5, 3, 2, 4, 7, 5, 9, 10, 8)
|
||||
val index = linearSearchArray(nums, target)
|
||||
println("目標要素 3 のインデックス = $index")
|
||||
|
||||
/* 連結リストで線形探索を行う */
|
||||
val head = ListNode.arrToLinkedList(nums)
|
||||
val node = linearSearchLinkedList(head, target)
|
||||
println("目標ノード値 3 に対応するノードオブジェクトは $node")
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
/**
|
||||
* File: two_sum.kt
|
||||
* Created Time: 2024-01-25
|
||||
* Author: curtishd (1023632660@qq.com)
|
||||
*/
|
||||
|
||||
package chapter_searching
|
||||
|
||||
/* 方法 1:総当たり列挙 */
|
||||
fun twoSumBruteForce(nums: IntArray, target: Int): IntArray {
|
||||
val size = nums.size
|
||||
// 2重ループのため、時間計算量は O(n^2)
|
||||
for (i in 0..<size - 1) {
|
||||
for (j in i + 1..<size) {
|
||||
if (nums[i] + nums[j] == target) return intArrayOf(i, j)
|
||||
}
|
||||
}
|
||||
return IntArray(0)
|
||||
}
|
||||
|
||||
/* 方法 2:補助ハッシュテーブル */
|
||||
fun twoSumHashTable(nums: IntArray, target: Int): IntArray {
|
||||
val size = nums.size
|
||||
// 補助ハッシュテーブルを使用し、空間計算量は O(n)
|
||||
val dic = HashMap<Int, Int>()
|
||||
// 単一ループで、時間計算量は O(n)
|
||||
for (i in 0..<size) {
|
||||
if (dic.containsKey(target - nums[i])) {
|
||||
return intArrayOf(dic[target - nums[i]]!!, i)
|
||||
}
|
||||
dic[nums[i]] = i
|
||||
}
|
||||
return IntArray(0)
|
||||
}
|
||||
|
||||
/* Driver Code */
|
||||
fun main() {
|
||||
// ======= Test Case =======
|
||||
val nums = intArrayOf(2, 7, 11, 15)
|
||||
val target = 13
|
||||
|
||||
// ====== Driver Code ======
|
||||
// 方法 1
|
||||
var res = twoSumBruteForce(nums, target)
|
||||
println("方法1 res = ${res.contentToString()}")
|
||||
// 方法 2
|
||||
res = twoSumHashTable(nums, target)
|
||||
println("方法2 res = ${res.contentToString()}")
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
/**
|
||||
* File: bubble_sort.kt
|
||||
* Created Time: 2024-01-25
|
||||
* Author: curtishd (1023632660@qq.com)
|
||||
*/
|
||||
|
||||
package chapter_sorting
|
||||
|
||||
/* バブルソート */
|
||||
fun bubbleSort(nums: IntArray) {
|
||||
// 外側のループ:未ソート区間は [0, i]
|
||||
for (i in nums.size - 1 downTo 1) {
|
||||
// 内側のループ:未ソート区間 [0, i] の最大要素をその区間の最右端へ交換
|
||||
for (j in 0..<i) {
|
||||
if (nums[j] > nums[j + 1]) {
|
||||
// nums[j] と nums[j + 1] を交換
|
||||
val temp = nums[j]
|
||||
nums[j] = nums[j + 1]
|
||||
nums[j + 1] = temp
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* バブルソート(フラグ最適化) */
|
||||
fun bubbleSortWithFlag(nums: IntArray) {
|
||||
// 外側のループ:未ソート区間は [0, i]
|
||||
for (i in nums.size - 1 downTo 1) {
|
||||
var flag = false // フラグを初期化する
|
||||
// 内側のループ:未ソート区間 [0, i] の最大要素をその区間の最右端へ交換
|
||||
for (j in 0..<i) {
|
||||
if (nums[j] > nums[j + 1]) {
|
||||
// nums[j] と nums[j + 1] を交換
|
||||
val temp = nums[j]
|
||||
nums[j] = nums[j + 1]
|
||||
nums[j + 1] = temp
|
||||
flag = true // 交換する要素を記録
|
||||
}
|
||||
}
|
||||
if (!flag) break // このバブル処理で要素交換が一度もなければそのまま終了
|
||||
}
|
||||
}
|
||||
|
||||
/* Driver Code */
|
||||
fun main() {
|
||||
val nums = intArrayOf(4, 1, 3, 1, 5, 2)
|
||||
bubbleSort(nums)
|
||||
println("バブルソート完了後 nums = ${nums.contentToString()}")
|
||||
|
||||
val nums1 = intArrayOf(4, 1, 3, 1, 5, 2)
|
||||
bubbleSortWithFlag(nums1)
|
||||
println("バブルソート完了後 nums1 = ${nums1.contentToString()}")
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
/**
|
||||
* File: bucket_sort.kt
|
||||
* Created Time: 2024-01-25
|
||||
* Author: curtishd (1023632660@qq.com)
|
||||
*/
|
||||
|
||||
package chapter_sorting
|
||||
|
||||
/* バケットソート */
|
||||
fun bucketSort(nums: FloatArray) {
|
||||
// k = n/2 個のバケットを初期化し、各バケットに 2 要素ずつ割り当てる想定とする
|
||||
val k = nums.size / 2
|
||||
val buckets = mutableListOf<MutableList<Float>>()
|
||||
for (i in 0..<k) {
|
||||
buckets.add(mutableListOf())
|
||||
}
|
||||
// 1. 配列要素を各バケットに振り分ける
|
||||
for (num in nums) {
|
||||
// 入力データの範囲は [0, 1) であり、num * k を用いてインデックス範囲 [0, k-1] に写像する
|
||||
val i = (num * k).toInt()
|
||||
// num をバケット i に追加
|
||||
buckets[i].add(num)
|
||||
}
|
||||
// 2. 各バケットをソートする
|
||||
for (bucket in buckets) {
|
||||
// 組み込みのソート関数を使う。他のソートアルゴリズムに置き換えてもよい
|
||||
bucket.sort()
|
||||
}
|
||||
// 3. バケットを走査して結果を結合
|
||||
var i = 0
|
||||
for (bucket in buckets) {
|
||||
for (num in bucket) {
|
||||
nums[i++] = num
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Driver Code */
|
||||
fun main() {
|
||||
// 入力データは範囲 [0, 1) の浮動小数点数とする
|
||||
val nums = floatArrayOf(0.49f, 0.96f, 0.82f, 0.09f, 0.57f, 0.43f, 0.91f, 0.75f, 0.15f, 0.37f)
|
||||
bucketSort(nums)
|
||||
println("バケットソート完了後 nums = ${nums.contentToString()}")
|
||||
}
|
||||
@@ -0,0 +1,80 @@
|
||||
/**
|
||||
* File: counting_sort.kt
|
||||
* Created Time: 2024-01-25
|
||||
* Author: curtishd (1023632660@qq.com)
|
||||
*/
|
||||
|
||||
package chapter_sorting
|
||||
|
||||
import kotlin.math.max
|
||||
|
||||
/* 計数ソート */
|
||||
// 簡易実装のため、オブジェクトのソートには使えない
|
||||
fun countingSortNaive(nums: IntArray) {
|
||||
// 1. 配列の最大要素 m を求める
|
||||
var m = 0
|
||||
for (num in nums) {
|
||||
m = max(m, num)
|
||||
}
|
||||
// 2. 各数値の出現回数を数える
|
||||
// counter[num] は num の出現回数を表す
|
||||
val counter = IntArray(m + 1)
|
||||
for (num in nums) {
|
||||
counter[num]++
|
||||
}
|
||||
// 3. counter を走査し、各要素を元の配列 nums に書き戻す
|
||||
var i = 0
|
||||
for (num in 0..<m + 1) {
|
||||
var j = 0
|
||||
while (j < counter[num]) {
|
||||
nums[i] = num
|
||||
j++
|
||||
i++
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* 計数ソート */
|
||||
// 完全な実装で、オブジェクトをソートでき、かつ安定ソートである
|
||||
fun countingSort(nums: IntArray) {
|
||||
// 1. 配列の最大要素 m を求める
|
||||
var m = 0
|
||||
for (num in nums) {
|
||||
m = max(m, num)
|
||||
}
|
||||
// 2. 各数値の出現回数を数える
|
||||
// counter[num] は num の出現回数を表す
|
||||
val counter = IntArray(m + 1)
|
||||
for (num in nums) {
|
||||
counter[num]++
|
||||
}
|
||||
// 3. counter の累積和を求めて、「出現回数」を「末尾インデックス」に変換する
|
||||
// つまり counter[num]-1 は、num が res に最後に現れるインデックス
|
||||
for (i in 0..<m) {
|
||||
counter[i + 1] += counter[i]
|
||||
}
|
||||
// 4. nums を逆順に走査し、各要素を結果配列 res に格納する
|
||||
// 結果を記録するための配列 res を初期化
|
||||
val n = nums.size
|
||||
val res = IntArray(n)
|
||||
for (i in n - 1 downTo 0) {
|
||||
val num = nums[i]
|
||||
res[counter[num] - 1] = num // num を対応するインデックスに配置
|
||||
counter[num]-- // 累積和を 1 減らして、次に num を配置するインデックスを得る
|
||||
}
|
||||
// 結果配列 res で元の配列 nums を上書きする
|
||||
for (i in 0..<n) {
|
||||
nums[i] = res[i]
|
||||
}
|
||||
}
|
||||
|
||||
/* Driver Code */
|
||||
fun main() {
|
||||
val nums = intArrayOf(1, 0, 1, 2, 0, 4, 0, 2, 2, 4)
|
||||
countingSortNaive(nums)
|
||||
println("カウントソート(オブジェクトはソート不可)完了後 nums = ${nums.contentToString()}")
|
||||
|
||||
val nums1 = intArrayOf(1, 0, 1, 2, 0, 4, 0, 2, 2, 4)
|
||||
countingSort(nums1)
|
||||
println("カウントソート完了後 nums1 = ${nums1.contentToString()}")
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
/**
|
||||
* File: heap_sort.kt
|
||||
* Created Time: 2024-01-25
|
||||
* Author: curtishd (1023632660@qq.com)
|
||||
*/
|
||||
|
||||
package chapter_sorting
|
||||
|
||||
/* ヒープの長さは n。ノード i から下方向にヒープ化 */
|
||||
fun siftDown(nums: IntArray, n: Int, li: Int) {
|
||||
var i = li
|
||||
while (true) {
|
||||
// ノード i, l, r のうち値が最大のノードを ma とする
|
||||
val l = 2 * i + 1
|
||||
val r = 2 * i + 2
|
||||
var ma = i
|
||||
if (l < n && nums[l] > nums[ma])
|
||||
ma = l
|
||||
if (r < n && nums[r] > nums[ma])
|
||||
ma = r
|
||||
// ノード i が最大、またはインデックス l, r が範囲外なら、ヒープ化は不要なので抜ける
|
||||
if (ma == i)
|
||||
break
|
||||
// 2 つのノードを交換
|
||||
val temp = nums[i]
|
||||
nums[i] = nums[ma]
|
||||
nums[ma] = temp
|
||||
// ループで上から下へヒープ化
|
||||
i = ma
|
||||
}
|
||||
}
|
||||
|
||||
/* ヒープソート */
|
||||
fun heapSort(nums: IntArray) {
|
||||
// ヒープ構築:葉ノード以外のすべてのノードをヒープ化する
|
||||
for (i in nums.size / 2 - 1 downTo 0) {
|
||||
siftDown(nums, nums.size, i)
|
||||
}
|
||||
// ヒープから最大要素を取り出し、n-1 回繰り返す
|
||||
for (i in nums.size - 1 downTo 1) {
|
||||
// 根ノードと最も右の葉ノードを交換(先頭要素と末尾要素を交換)
|
||||
val temp = nums[0]
|
||||
nums[0] = nums[i]
|
||||
nums[i] = temp
|
||||
// 根ノードを起点に、上から下へヒープ化
|
||||
siftDown(nums, i, 0)
|
||||
}
|
||||
}
|
||||
|
||||
/* Driver Code */
|
||||
fun main() {
|
||||
val nums = intArrayOf(4, 1, 3, 1, 5, 2)
|
||||
heapSort(nums)
|
||||
println("ヒープソート完了後 nums = ${nums.contentToString()}")
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
/**
|
||||
* File: insertion_sort.kt
|
||||
* Created Time: 2024-01-25
|
||||
* Author: curtishd (1023632660@qq.com)
|
||||
*/
|
||||
|
||||
package chapter_sorting
|
||||
|
||||
/* 挿入ソート */
|
||||
fun insertionSort(nums: IntArray) {
|
||||
// 外側ループ: ソート済み要素は 1, 2, ..., n
|
||||
for (i in nums.indices) {
|
||||
val base = nums[i]
|
||||
var j = i - 1
|
||||
// 内側ループ: base をソート済み区間 [0, i-1] の正しい位置に挿入する
|
||||
while (j >= 0 && nums[j] > base) {
|
||||
nums[j + 1] = nums[j] // nums[j] を 1 つ右へ移動する
|
||||
j--
|
||||
}
|
||||
nums[j + 1] = base // base を正しい位置に配置する
|
||||
}
|
||||
}
|
||||
|
||||
/* Driver Code */
|
||||
fun main() {
|
||||
val nums = intArrayOf(4, 1, 3, 1, 5, 2)
|
||||
insertionSort(nums)
|
||||
println("挿入ソート完了後 nums = ${nums.contentToString()}")
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
/**
|
||||
* File: merge_sort.kt
|
||||
* Created Time: 2024-01-25
|
||||
* Author: curtishd (1023632660@qq.com)
|
||||
*/
|
||||
|
||||
package chapter_sorting
|
||||
|
||||
/* 左部分配列と右部分配列をマージ */
|
||||
fun merge(nums: IntArray, left: Int, mid: Int, right: Int) {
|
||||
// 左部分配列の区間は [left, mid]、右部分配列の区間は [mid+1, right]
|
||||
// マージ結果を格納する一時配列 tmp を作成
|
||||
val tmp = IntArray(right - left + 1)
|
||||
// 左右の部分配列の開始インデックスを初期化する
|
||||
var i = left
|
||||
var j = mid + 1
|
||||
var k = 0
|
||||
// 左右の部分配列にまだ要素がある間は比較し、小さいほうを一時配列にコピーする
|
||||
while (i <= mid && j <= right) {
|
||||
if (nums[i] <= nums[j])
|
||||
tmp[k++] = nums[i++]
|
||||
else
|
||||
tmp[k++] = nums[j++]
|
||||
}
|
||||
// 左右の部分配列の残り要素を一時配列にコピーする
|
||||
while (i <= mid) {
|
||||
tmp[k++] = nums[i++]
|
||||
}
|
||||
while (j <= right) {
|
||||
tmp[k++] = nums[j++]
|
||||
}
|
||||
// 一時配列 tmp の要素を元の配列 nums の対応区間にコピーする
|
||||
for (l in tmp.indices) {
|
||||
nums[left + l] = tmp[l]
|
||||
}
|
||||
}
|
||||
|
||||
/* マージソート */
|
||||
fun mergeSort(nums: IntArray, left: Int, right: Int) {
|
||||
// 終了条件
|
||||
if (left >= right) return // 部分配列の長さが 1 になったら再帰を終了
|
||||
// 分割フェーズ
|
||||
val mid = left + (right - left) / 2 // 中点を計算
|
||||
mergeSort(nums, left, mid) // 左部分配列を再帰処理
|
||||
mergeSort(nums, mid + 1, right) // 右部分配列を再帰処理
|
||||
// マージフェーズ
|
||||
merge(nums, left, mid, right)
|
||||
}
|
||||
|
||||
/* Driver Code */
|
||||
fun main() {
|
||||
/* マージソート */
|
||||
val nums = intArrayOf(7, 3, 2, 6, 0, 1, 5, 4)
|
||||
mergeSort(nums, 0, nums.size - 1)
|
||||
println("マージソート完了後 nums = ${nums.contentToString()}")
|
||||
}
|
||||
@@ -0,0 +1,121 @@
|
||||
/**
|
||||
* File: quick_sort.kt
|
||||
* Created Time: 2024-01-25
|
||||
* Author: curtishd (1023632660@qq.com)
|
||||
*/
|
||||
|
||||
package chapter_sorting
|
||||
|
||||
/* 要素の交換 */
|
||||
fun swap(nums: IntArray, i: Int, j: Int) {
|
||||
val temp = nums[i]
|
||||
nums[i] = nums[j]
|
||||
nums[j] = temp
|
||||
}
|
||||
|
||||
/* 番兵分割 */
|
||||
fun partition(nums: IntArray, left: Int, right: Int): Int {
|
||||
// nums[left] を基準値とする
|
||||
var i = left
|
||||
var j = right
|
||||
while (i < j) {
|
||||
while (i < j && nums[j] >= nums[left])
|
||||
j-- // 右から左へ基準値未満の最初の要素を探す
|
||||
while (i < j && nums[i] <= nums[left])
|
||||
i++ // 左から右へ基準値より大きい最初の要素を探す
|
||||
swap(nums, i, j) // この 2 つの要素を交換
|
||||
}
|
||||
swap(nums, i, left) // 基準値を 2 つの部分配列の境界へ交換する
|
||||
return i // 基準値のインデックスを返す
|
||||
}
|
||||
|
||||
/* クイックソート */
|
||||
fun quickSort(nums: IntArray, left: Int, right: Int) {
|
||||
// 部分配列の長さが 1 なら再帰を終了する
|
||||
if (left >= right) return
|
||||
// 番兵分割
|
||||
val pivot = partition(nums, left, right)
|
||||
// 左右の部分配列を再帰処理
|
||||
quickSort(nums, left, pivot - 1)
|
||||
quickSort(nums, pivot + 1, right)
|
||||
}
|
||||
|
||||
/* 3つの候補要素の中央値を選ぶ */
|
||||
fun medianThree(nums: IntArray, left: Int, mid: Int, right: Int): Int {
|
||||
val l = nums[left]
|
||||
val m = nums[mid]
|
||||
val r = nums[right]
|
||||
if ((m in l..r) || (m in r..l))
|
||||
return mid // m は l と r の間
|
||||
if ((l in m..r) || (l in r..m))
|
||||
return left // l は m と r の間
|
||||
return right
|
||||
}
|
||||
|
||||
/* 番兵による分割処理(3 点中央値) */
|
||||
fun partitionMedian(nums: IntArray, left: Int, right: Int): Int {
|
||||
// 3つの候補要素の中央値を選ぶ
|
||||
val med = medianThree(nums, left, (left + right) / 2, right)
|
||||
// 中央値を配列の最左端に交換する
|
||||
swap(nums, left, med)
|
||||
// nums[left] を基準値とする
|
||||
var i = left
|
||||
var j = right
|
||||
while (i < j) {
|
||||
while (i < j && nums[j] >= nums[left])
|
||||
j-- // 右から左へ基準値未満の最初の要素を探す
|
||||
while (i < j && nums[i] <= nums[left])
|
||||
i++ // 左から右へ基準値より大きい最初の要素を探す
|
||||
swap(nums, i, j) // この 2 つの要素を交換
|
||||
}
|
||||
swap(nums, i, left) // 基準値を 2 つの部分配列の境界へ交換する
|
||||
return i // 基準値のインデックスを返す
|
||||
}
|
||||
|
||||
/* クイックソート */
|
||||
fun quickSortMedian(nums: IntArray, left: Int, right: Int) {
|
||||
// 部分配列の長さが 1 なら再帰を終了する
|
||||
if (left >= right) return
|
||||
// 番兵分割
|
||||
val pivot = partitionMedian(nums, left, right)
|
||||
// 左右の部分配列を再帰処理
|
||||
quickSort(nums, left, pivot - 1)
|
||||
quickSort(nums, pivot + 1, right)
|
||||
}
|
||||
|
||||
/* クイックソート(再帰深度最適化) */
|
||||
fun quickSortTailCall(nums: IntArray, left: Int, right: Int) {
|
||||
// 部分配列の長さが 1 なら終了
|
||||
var l = left
|
||||
var r = right
|
||||
while (l < r) {
|
||||
// 番兵による分割処理
|
||||
val pivot = partition(nums, l, r)
|
||||
// 2 つの部分配列のうち短いほうにクイックソートを適用する
|
||||
if (pivot - l < r - pivot) {
|
||||
quickSort(nums, l, pivot - 1) // 左部分配列を再帰的にソート
|
||||
l = pivot + 1 // 未ソート区間の残りは [pivot + 1, right]
|
||||
} else {
|
||||
quickSort(nums, pivot + 1, r) // 右部分配列を再帰的にソート
|
||||
r = pivot - 1 // 未ソート区間の残りは [left, pivot - 1]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Driver Code */
|
||||
fun main() {
|
||||
/* クイックソート */
|
||||
val nums = intArrayOf(2, 4, 1, 0, 3, 5)
|
||||
quickSort(nums, 0, nums.size - 1)
|
||||
println("クイックソート完了後 nums = ${nums.contentToString()}")
|
||||
|
||||
/* クイックソート(中央値の基準値で最適化) */
|
||||
val nums1 = intArrayOf(2, 4, 1, 0, 3, 5)
|
||||
quickSortMedian(nums1, 0, nums1.size - 1)
|
||||
println("クイックソート(中央値ピボット最適化)完了後 nums1 = ${nums1.contentToString()}")
|
||||
|
||||
/* クイックソート(再帰深度最適化) */
|
||||
val nums2 = intArrayOf(2, 4, 1, 0, 3, 5)
|
||||
quickSortTailCall(nums2, 0, nums2.size - 1)
|
||||
println("クイックソート(再帰深度最適化)完了後 nums2 = ${nums2.contentToString()}")
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
/**
|
||||
* File: radix_sort.kt
|
||||
* Created Time: 2024-01-25
|
||||
* Author: curtishd (1023632660@qq.com)
|
||||
*/
|
||||
|
||||
package chapter_sorting
|
||||
|
||||
/* 要素 num の下から k 桁目を取得(exp = 10^(k-1)) */
|
||||
fun digit(num: Int, exp: Int): Int {
|
||||
// ここで高コストな累乗計算を繰り返さないよう、k ではなく exp を渡す
|
||||
return (num / exp) % 10
|
||||
}
|
||||
|
||||
/* 計数ソート(nums の k 桁目でソート) */
|
||||
fun countingSortDigit(nums: IntArray, exp: Int) {
|
||||
// 10 進数の各桁は 0~9 の範囲なので、長さ 10 のバケット配列が必要
|
||||
val counter = IntArray(10)
|
||||
val n = nums.size
|
||||
// 0~9 の各数字の出現回数を集計する
|
||||
for (i in 0..<n) {
|
||||
val d = digit(nums[i], exp) // nums[i] の第 k 位を取得し、d とする
|
||||
counter[d]++ // 数字 d の出現回数を数える
|
||||
}
|
||||
// 累積和を求め、「出現回数」を「配列インデックス」に変換する
|
||||
for (i in 1..9) {
|
||||
counter[i] += counter[i - 1]
|
||||
}
|
||||
// 逆順に走査し、バケット内の集計結果に従って各要素を res に格納する
|
||||
val res = IntArray(n)
|
||||
for (i in n - 1 downTo 0) {
|
||||
val d = digit(nums[i], exp)
|
||||
val j = counter[d] - 1 // d の配列内インデックス j を取得する
|
||||
res[j] = nums[i] // 現在の要素をインデックス j に格納する
|
||||
counter[d]-- // d の個数を 1 減らす
|
||||
}
|
||||
// 結果で元の配列 nums を上書きする
|
||||
for (i in 0..<n)
|
||||
nums[i] = res[i]
|
||||
}
|
||||
|
||||
/* 基数ソート */
|
||||
fun radixSort(nums: IntArray) {
|
||||
// 最大桁数の判定用に配列の最大要素を取得
|
||||
var m = Int.MIN_VALUE
|
||||
for (num in nums) if (num > m) m = num
|
||||
var exp = 1
|
||||
// 下位桁から上位桁の順に走査する
|
||||
while (exp <= m) {
|
||||
// 配列要素の k 桁目に対して計数ソートを行う
|
||||
// k = 1 -> exp = 1
|
||||
// k = 2 -> exp = 10
|
||||
// つまり exp = 10^(k-1)
|
||||
countingSortDigit(nums, exp)
|
||||
exp *= 10
|
||||
}
|
||||
}
|
||||
|
||||
/* Driver Code */
|
||||
fun main() {
|
||||
// 基数ソート
|
||||
val nums = intArrayOf(
|
||||
10546151, 35663510, 42865989, 34862445, 81883077,
|
||||
88906420, 72429244, 30524779, 82060337, 63832996
|
||||
)
|
||||
radixSort(nums)
|
||||
println("基数ソート完了後 nums = ${nums.contentToString()}")
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
/**
|
||||
* File: selection_sort.kt
|
||||
* Created Time: 2024-01-25
|
||||
* Author: curtishd (1023632660@qq.com)
|
||||
*/
|
||||
|
||||
package chapter_sorting
|
||||
|
||||
/* 選択ソート */
|
||||
fun selectionSort(nums: IntArray) {
|
||||
val n = nums.size
|
||||
// 外側ループ:未整列区間は [i, n-1]
|
||||
for (i in 0..<n - 1) {
|
||||
var k = i
|
||||
// 内側のループ:未ソート区間の最小要素を見つける
|
||||
for (j in i + 1..<n) {
|
||||
if (nums[j] < nums[k])
|
||||
k = j // 最小要素のインデックスを記録
|
||||
}
|
||||
// その最小要素を未整列区間の先頭要素と交換する
|
||||
val temp = nums[i]
|
||||
nums[i] = nums[k]
|
||||
nums[k] = temp
|
||||
}
|
||||
}
|
||||
|
||||
/* Driver Code */
|
||||
fun main() {
|
||||
val nums = intArrayOf(4, 1, 3, 1, 5, 2)
|
||||
selectionSort(nums)
|
||||
println("選択ソート完了後 nums = ${nums.contentToString()}")
|
||||
}
|
||||
@@ -0,0 +1,145 @@
|
||||
/**
|
||||
* File: array_deque.kt
|
||||
* Created Time: 2024-01-25
|
||||
* Author: curtishd (1023632660@qq.com)
|
||||
*/
|
||||
|
||||
package chapter_stack_and_queue
|
||||
|
||||
/* 循環配列ベースの両端キュー */
|
||||
/* コンストラクタ */
|
||||
class ArrayDeque(capacity: Int) {
|
||||
private var nums: IntArray = IntArray(capacity) // 両端キューの要素を格納する配列
|
||||
private var front: Int = 0 // 先頭ポインタ。先頭要素を指す
|
||||
private var queSize: Int = 0 // 両端キューの長さ
|
||||
|
||||
/* 両端キューの容量を取得 */
|
||||
fun capacity(): Int {
|
||||
return nums.size
|
||||
}
|
||||
|
||||
/* 両端キューの長さを取得 */
|
||||
fun size(): Int {
|
||||
return queSize
|
||||
}
|
||||
|
||||
/* 両端キューが空かどうかを判定 */
|
||||
fun isEmpty(): Boolean {
|
||||
return queSize == 0
|
||||
}
|
||||
|
||||
/* 循環配列のインデックスを計算 */
|
||||
private fun index(i: Int): Int {
|
||||
// 剰余演算により配列の先頭と末尾をつなげる
|
||||
// i が配列の末尾を越えたら先頭に戻る
|
||||
// i が配列の先頭を越えて前に出たら末尾に戻る
|
||||
return (i + capacity()) % capacity()
|
||||
}
|
||||
|
||||
/* キュー先頭にエンキュー */
|
||||
fun pushFirst(num: Int) {
|
||||
if (queSize == capacity()) {
|
||||
println("双方向キューは満杯です")
|
||||
return
|
||||
}
|
||||
// 先頭ポインタを左に 1 つ移動する
|
||||
// 剰余演算により、front が配列先頭を越えた後に末尾へ戻るようにする
|
||||
front = index(front - 1)
|
||||
// num をキュー先頭に追加
|
||||
nums[front] = num
|
||||
queSize++
|
||||
}
|
||||
|
||||
/* キュー末尾にエンキュー */
|
||||
fun pushLast(num: Int) {
|
||||
if (queSize == capacity()) {
|
||||
println("双方向キューは満杯です")
|
||||
return
|
||||
}
|
||||
// キュー末尾ポインタを計算し、末尾インデックス + 1 を指す
|
||||
val rear = index(front + queSize)
|
||||
// num をキュー末尾に追加
|
||||
nums[rear] = num
|
||||
queSize++
|
||||
}
|
||||
|
||||
/* キュー先頭からデキュー */
|
||||
fun popFirst(): Int {
|
||||
val num = peekFirst()
|
||||
// 先頭ポインタを 1 つ後ろへ進める
|
||||
front = index(front + 1)
|
||||
queSize--
|
||||
return num
|
||||
}
|
||||
|
||||
/* キュー末尾からデキュー */
|
||||
fun popLast(): Int {
|
||||
val num = peekLast()
|
||||
queSize--
|
||||
return num
|
||||
}
|
||||
|
||||
/* キュー先頭の要素にアクセス */
|
||||
fun peekFirst(): Int {
|
||||
if (isEmpty()) throw IndexOutOfBoundsException()
|
||||
return nums[front]
|
||||
}
|
||||
|
||||
/* キュー末尾の要素にアクセス */
|
||||
fun peekLast(): Int {
|
||||
if (isEmpty()) throw IndexOutOfBoundsException()
|
||||
// 末尾要素のインデックスを計算
|
||||
val last = index(front + queSize - 1)
|
||||
return nums[last]
|
||||
}
|
||||
|
||||
/* 出力用の配列を返す */
|
||||
fun toArray(): IntArray {
|
||||
// 有効長の範囲内のリスト要素のみを変換
|
||||
val res = IntArray(queSize)
|
||||
var i = 0
|
||||
var j = front
|
||||
while (i < queSize) {
|
||||
res[i] = nums[index(j)]
|
||||
i++
|
||||
j++
|
||||
}
|
||||
return res
|
||||
}
|
||||
}
|
||||
|
||||
/* Driver Code */
|
||||
fun main() {
|
||||
/* 両端キューを初期化 */
|
||||
val deque = ArrayDeque(10)
|
||||
deque.pushLast(3)
|
||||
deque.pushLast(2)
|
||||
deque.pushLast(5)
|
||||
println("双方向キュー deque = ${deque.toArray().contentToString()}")
|
||||
|
||||
/* 要素にアクセス */
|
||||
val peekFirst = deque.peekFirst()
|
||||
println("先頭要素 peekFirst = $peekFirst")
|
||||
val peekLast = deque.peekLast()
|
||||
println("末尾要素 peekLast = $peekLast")
|
||||
|
||||
/* 要素をエンキュー */
|
||||
deque.pushLast(4)
|
||||
println("要素 4 を末尾に追加した後 deque = ${deque.toArray().contentToString()}")
|
||||
deque.pushFirst(1)
|
||||
println("要素 1 を先頭に追加した後 deque = ${deque.toArray().contentToString()}")
|
||||
|
||||
/* 要素をデキュー */
|
||||
val popLast = deque.popLast()
|
||||
println("末尾から取り出した要素 = ${popLast}、末尾から取り出した後 deque = ${deque.toArray().contentToString()}")
|
||||
val popFirst = deque.popFirst()
|
||||
println("先頭から取り出した要素 = ${popFirst}、先頭から取り出した後 deque = ${deque.toArray().contentToString()}")
|
||||
|
||||
/* 両端キューの長さを取得 */
|
||||
val size = deque.size()
|
||||
println("双方向キューの長さ size = $size")
|
||||
|
||||
/* 両端キューが空かどうかを判定 */
|
||||
val isEmpty = deque.isEmpty()
|
||||
println("双方向キューが空かどうか = $isEmpty")
|
||||
}
|
||||
@@ -0,0 +1,110 @@
|
||||
/**
|
||||
* File: array_queue.kt
|
||||
* Created Time: 2024-01-25
|
||||
* Author: curtishd (1023632660@qq.com)
|
||||
*/
|
||||
|
||||
package chapter_stack_and_queue
|
||||
|
||||
/* 循環配列ベースのキュー */
|
||||
class ArrayQueue(capacity: Int) {
|
||||
private val nums: IntArray = IntArray(capacity) // キュー要素を格納する配列
|
||||
private var front: Int = 0 // 先頭ポインタ。先頭要素を指す
|
||||
private var queSize: Int = 0 // キューの長さ
|
||||
|
||||
/* キューの容量を取得 */
|
||||
fun capacity(): Int {
|
||||
return nums.size
|
||||
}
|
||||
|
||||
/* キューの長さを取得 */
|
||||
fun size(): Int {
|
||||
return queSize
|
||||
}
|
||||
|
||||
/* キューが空かどうかを判定 */
|
||||
fun isEmpty(): Boolean {
|
||||
return queSize == 0
|
||||
}
|
||||
|
||||
/* エンキュー */
|
||||
fun push(num: Int) {
|
||||
if (queSize == capacity()) {
|
||||
println("キューは満杯です")
|
||||
return
|
||||
}
|
||||
// 末尾ポインタを計算し、末尾インデックス + 1 を指す
|
||||
// 剰余演算により、rear が配列末尾を越えた後に先頭へ戻るようにする
|
||||
val rear = (front + queSize) % capacity()
|
||||
// num をキュー末尾に追加
|
||||
nums[rear] = num
|
||||
queSize++
|
||||
}
|
||||
|
||||
/* デキュー */
|
||||
fun pop(): Int {
|
||||
val num = peek()
|
||||
// 先頭ポインタを1つ後ろへ進め、末尾を越えたら配列先頭に戻す
|
||||
front = (front + 1) % capacity()
|
||||
queSize--
|
||||
return num
|
||||
}
|
||||
|
||||
/* キュー先頭の要素にアクセス */
|
||||
fun peek(): Int {
|
||||
if (isEmpty()) throw IndexOutOfBoundsException()
|
||||
return nums[front]
|
||||
}
|
||||
|
||||
/* 配列を返す */
|
||||
fun toArray(): IntArray {
|
||||
// 有効長の範囲内のリスト要素のみを変換
|
||||
val res = IntArray(queSize)
|
||||
var i = 0
|
||||
var j = front
|
||||
while (i < queSize) {
|
||||
res[i] = nums[j % capacity()]
|
||||
i++
|
||||
j++
|
||||
}
|
||||
return res
|
||||
}
|
||||
}
|
||||
|
||||
/* Driver Code */
|
||||
fun main() {
|
||||
/* キューを初期化 */
|
||||
val capacity = 10
|
||||
val queue = ArrayQueue(capacity)
|
||||
|
||||
/* 要素をエンキュー */
|
||||
queue.push(1)
|
||||
queue.push(3)
|
||||
queue.push(2)
|
||||
queue.push(5)
|
||||
queue.push(4)
|
||||
println("キュー queue = ${queue.toArray().contentToString()}")
|
||||
|
||||
/* キュー先頭の要素にアクセス */
|
||||
val peek = queue.peek()
|
||||
println("先頭要素 peek = $peek")
|
||||
|
||||
/* 要素をデキュー */
|
||||
val pop = queue.pop()
|
||||
println("取り出した要素 pop = ${pop}、取り出した後 queue = ${queue.toArray().contentToString()}")
|
||||
|
||||
/* キューの長さを取得 */
|
||||
val size = queue.size()
|
||||
println("キューの長さ size = $size")
|
||||
|
||||
/* キューが空かどうかを判定 */
|
||||
val isEmpty = queue.isEmpty()
|
||||
println("キューが空かどうか = $isEmpty")
|
||||
|
||||
/* 循環配列をテストする */
|
||||
for (i in 0..9) {
|
||||
queue.push(i)
|
||||
queue.pop()
|
||||
println("第 $i ラウンドの追加 + 取り出し後 queue = ${queue.toArray().contentToString()}")
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
/**
|
||||
* File: array_stack.kt
|
||||
* Created Time: 2024-01-25
|
||||
* Author: curtishd (1023632660@qq.com)
|
||||
*/
|
||||
|
||||
package chapter_stack_and_queue
|
||||
|
||||
/* 配列ベースのスタック */
|
||||
class ArrayStack {
|
||||
// リスト(動的配列)を初期化する
|
||||
private val stack = mutableListOf<Int>()
|
||||
|
||||
/* スタックの長さを取得 */
|
||||
fun size(): Int {
|
||||
return stack.size
|
||||
}
|
||||
|
||||
/* スタックが空かどうかを判定 */
|
||||
fun isEmpty(): Boolean {
|
||||
return size() == 0
|
||||
}
|
||||
|
||||
/* プッシュ */
|
||||
fun push(num: Int) {
|
||||
stack.add(num)
|
||||
}
|
||||
|
||||
/* ポップ */
|
||||
fun pop(): Int {
|
||||
if (isEmpty()) throw IndexOutOfBoundsException()
|
||||
return stack.removeAt(size() - 1)
|
||||
}
|
||||
|
||||
/* スタックトップの要素にアクセス */
|
||||
fun peek(): Int {
|
||||
if (isEmpty()) throw IndexOutOfBoundsException()
|
||||
return stack[size() - 1]
|
||||
}
|
||||
|
||||
/* List を Array に変換して返す */
|
||||
fun toArray(): Array<Any> {
|
||||
return stack.toTypedArray()
|
||||
}
|
||||
}
|
||||
|
||||
/* Driver Code */
|
||||
fun main() {
|
||||
/* スタックを初期化 */
|
||||
val stack = ArrayStack()
|
||||
|
||||
/* 要素をプッシュ */
|
||||
stack.push(1)
|
||||
stack.push(3)
|
||||
stack.push(2)
|
||||
stack.push(5)
|
||||
stack.push(4)
|
||||
println("スタック stack = ${stack.toArray().contentToString()}")
|
||||
|
||||
/* スタックトップの要素にアクセス */
|
||||
val peek = stack.peek()
|
||||
println("スタックトップ要素 peek = $peek")
|
||||
|
||||
/* 要素をポップ */
|
||||
val pop = stack.pop()
|
||||
println("ポップした要素 pop = $pop、ポップ後の stack = ${stack.toArray().contentToString()}")
|
||||
|
||||
/* スタックの長さを取得 */
|
||||
val size = stack.size()
|
||||
println("スタックの長さ size = $size")
|
||||
|
||||
/* 空かどうかを判定 */
|
||||
val isEmpty = stack.isEmpty()
|
||||
println("スタックが空かどうか = $isEmpty")
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
/**
|
||||
* File: deque.kt
|
||||
* Created Time: 2024-01-25
|
||||
* Author: curtishd (1023632660@qq.com)
|
||||
*/
|
||||
|
||||
package chapter_stack_and_queue
|
||||
|
||||
import java.util.*
|
||||
|
||||
/* Driver Code */
|
||||
fun main() {
|
||||
/* 両端キューを初期化 */
|
||||
val deque = LinkedList<Int>()
|
||||
deque.offerLast(3)
|
||||
deque.offerLast(2)
|
||||
deque.offerLast(5)
|
||||
println("両端キュー deque = $deque")
|
||||
|
||||
/* 要素にアクセス */
|
||||
val peekFirst = deque.peekFirst()
|
||||
println("先頭要素 peekFirst = $peekFirst")
|
||||
val peekLast = deque.peekLast()
|
||||
println("末尾要素 peekLast = $peekLast")
|
||||
|
||||
/* 要素をエンキュー */
|
||||
deque.offerLast(4)
|
||||
println("要素 4 を末尾に追加後 deque = $deque")
|
||||
deque.offerFirst(1)
|
||||
println("要素 1 を先頭に追加後 deque = $deque")
|
||||
|
||||
/* 要素をデキュー */
|
||||
val popLast = deque.pollLast()
|
||||
println("末尾から取り出した要素 = $popLast、取り出し後 deque = $deque")
|
||||
val popFirst = deque.pollFirst()
|
||||
println("先頭から取り出した要素 = $popFirst、取り出し後 deque = $deque")
|
||||
|
||||
/* 両端キューの長さを取得 */
|
||||
val size = deque.size
|
||||
println("双方向キューの長さ size = $size")
|
||||
|
||||
/* 両端キューが空かどうかを判定 */
|
||||
val isEmpty = deque.isEmpty()
|
||||
println("双方向キューが空かどうか = $isEmpty")
|
||||
}
|
||||
@@ -0,0 +1,163 @@
|
||||
/**
|
||||
* File: linkedlist_deque.kt
|
||||
* Created Time: 2024-01-25
|
||||
* Author: curtishd (1023632660@qq.com)
|
||||
*/
|
||||
|
||||
package chapter_stack_and_queue
|
||||
|
||||
/* 双方向連結リストノード */
|
||||
class ListNode(var _val: Int) {
|
||||
// ノード値
|
||||
var next: ListNode? = null // 後続ノードへの参照
|
||||
var prev: ListNode? = null // 前駆ノードへの参照
|
||||
}
|
||||
|
||||
/* 双方向連結リストベースの両端キュー */
|
||||
class LinkedListDeque {
|
||||
private var front: ListNode? = null // 先頭ノード front
|
||||
private var rear: ListNode? = null // 末尾ノード rear
|
||||
private var queSize: Int = 0 // 両端キューの長さ
|
||||
|
||||
/* 両端キューの長さを取得 */
|
||||
fun size(): Int {
|
||||
return queSize
|
||||
}
|
||||
|
||||
/* 両端キューが空かどうかを判定 */
|
||||
fun isEmpty(): Boolean {
|
||||
return size() == 0
|
||||
}
|
||||
|
||||
/* エンキュー操作 */
|
||||
fun push(num: Int, isFront: Boolean) {
|
||||
val node = ListNode(num)
|
||||
// 連結リストが空なら、front と rear の両方を node に向ける
|
||||
if (isEmpty()) {
|
||||
rear = node
|
||||
front = rear
|
||||
// 先頭へのエンキュー操作
|
||||
} else if (isFront) {
|
||||
// node を連結リストの先頭に追加
|
||||
front?.prev = node
|
||||
node.next = front
|
||||
front = node // 先頭ノードを更新する
|
||||
// 末尾へのエンキュー操作
|
||||
} else {
|
||||
// node を連結リストの末尾に追加
|
||||
rear?.next = node
|
||||
node.prev = rear
|
||||
rear = node // 末尾ノードを更新する
|
||||
}
|
||||
queSize++ // キューの長さを更新
|
||||
}
|
||||
|
||||
/* キュー先頭にエンキュー */
|
||||
fun pushFirst(num: Int) {
|
||||
push(num, true)
|
||||
}
|
||||
|
||||
/* キュー末尾にエンキュー */
|
||||
fun pushLast(num: Int) {
|
||||
push(num, false)
|
||||
}
|
||||
|
||||
/* デキュー操作 */
|
||||
fun pop(isFront: Boolean): Int {
|
||||
if (isEmpty())
|
||||
throw IndexOutOfBoundsException()
|
||||
val _val: Int
|
||||
// キュー先頭からの取り出し
|
||||
if (isFront) {
|
||||
_val = front!!._val // 先頭ノードの値を一時保存
|
||||
// 先頭ノードを削除
|
||||
val fNext = front!!.next
|
||||
if (fNext != null) {
|
||||
fNext.prev = null
|
||||
front!!.next = null
|
||||
}
|
||||
front = fNext // 先頭ノードを更新する
|
||||
// キュー末尾からの取り出し
|
||||
} else {
|
||||
_val = rear!!._val // 末尾ノードの値を一時保存
|
||||
// 末尾ノードを削除
|
||||
val rPrev = rear!!.prev
|
||||
if (rPrev != null) {
|
||||
rPrev.next = null
|
||||
rear!!.prev = null
|
||||
}
|
||||
rear = rPrev // 末尾ノードを更新する
|
||||
}
|
||||
queSize-- // キューの長さを更新
|
||||
return _val
|
||||
}
|
||||
|
||||
/* キュー先頭からデキュー */
|
||||
fun popFirst(): Int {
|
||||
return pop(true)
|
||||
}
|
||||
|
||||
/* キュー末尾からデキュー */
|
||||
fun popLast(): Int {
|
||||
return pop(false)
|
||||
}
|
||||
|
||||
/* キュー先頭の要素にアクセス */
|
||||
fun peekFirst(): Int {
|
||||
if (isEmpty()) throw IndexOutOfBoundsException()
|
||||
return front!!._val
|
||||
}
|
||||
|
||||
/* キュー末尾の要素にアクセス */
|
||||
fun peekLast(): Int {
|
||||
if (isEmpty()) throw IndexOutOfBoundsException()
|
||||
return rear!!._val
|
||||
}
|
||||
|
||||
/* 出力用の配列を返す */
|
||||
fun toArray(): IntArray {
|
||||
var node = front
|
||||
val res = IntArray(size())
|
||||
for (i in res.indices) {
|
||||
res[i] = node!!._val
|
||||
node = node.next
|
||||
}
|
||||
return res
|
||||
}
|
||||
}
|
||||
|
||||
/* Driver Code */
|
||||
fun main() {
|
||||
/* 両端キューを初期化 */
|
||||
val deque = LinkedListDeque()
|
||||
deque.pushLast(3)
|
||||
deque.pushLast(2)
|
||||
deque.pushLast(5)
|
||||
println("双方向キュー deque = ${deque.toArray().contentToString()}")
|
||||
|
||||
/* 要素にアクセス */
|
||||
val peekFirst = deque.peekFirst()
|
||||
println("先頭要素 peekFirst = $peekFirst")
|
||||
val peekLast = deque.peekLast()
|
||||
println("末尾要素 peekLast = $peekLast")
|
||||
|
||||
/* 要素をエンキュー */
|
||||
deque.pushLast(4)
|
||||
println("要素 4 を末尾に追加した後 deque = ${deque.toArray().contentToString()}")
|
||||
deque.pushFirst(1)
|
||||
println("要素 1 を先頭に追加した後 deque = ${deque.toArray().contentToString()}")
|
||||
|
||||
/* 要素をデキュー */
|
||||
val popLast = deque.popLast()
|
||||
println("末尾から取り出した要素 = ${popLast}、末尾から取り出した後 deque = ${deque.toArray().contentToString()}")
|
||||
val popFirst = deque.popFirst()
|
||||
println("先頭から取り出した要素 = ${popFirst}、先頭から取り出した後 deque = ${deque.toArray().contentToString()}")
|
||||
|
||||
/* 両端キューの長さを取得 */
|
||||
val size = deque.size()
|
||||
println("双方向キューの長さ size = $size")
|
||||
|
||||
/* 両端キューが空かどうかを判定 */
|
||||
val isEmpty = deque.isEmpty()
|
||||
println("双方向キューが空かどうか = $isEmpty")
|
||||
}
|
||||
@@ -0,0 +1,98 @@
|
||||
/**
|
||||
* File: linkedlist_queue.kt
|
||||
* Created Time: 2024-01-25
|
||||
* Author: curtishd (1023632660@qq.com)
|
||||
*/
|
||||
|
||||
package chapter_stack_and_queue
|
||||
|
||||
/* 連結リストベースのキュー */
|
||||
class LinkedListQueue(
|
||||
// 先頭ノード front、末尾ノード rear
|
||||
private var front: ListNode? = null,
|
||||
private var rear: ListNode? = null,
|
||||
private var queSize: Int = 0
|
||||
) {
|
||||
|
||||
/* キューの長さを取得 */
|
||||
fun size(): Int {
|
||||
return queSize
|
||||
}
|
||||
|
||||
/* キューが空かどうかを判定 */
|
||||
fun isEmpty(): Boolean {
|
||||
return size() == 0
|
||||
}
|
||||
|
||||
/* エンキュー */
|
||||
fun push(num: Int) {
|
||||
// 末尾ノードの後ろに num を追加
|
||||
val node = ListNode(num)
|
||||
// キューが空なら、先頭・末尾ノードをともにそのノードに設定
|
||||
if (front == null) {
|
||||
front = node
|
||||
rear = node
|
||||
// キューが空でなければ、そのノードを末尾ノードの後ろに追加
|
||||
} else {
|
||||
rear?.next = node
|
||||
rear = node
|
||||
}
|
||||
queSize++
|
||||
}
|
||||
|
||||
/* デキュー */
|
||||
fun pop(): Int {
|
||||
val num = peek()
|
||||
// 先頭ノードを削除
|
||||
front = front?.next
|
||||
queSize--
|
||||
return num
|
||||
}
|
||||
|
||||
/* キュー先頭の要素にアクセス */
|
||||
fun peek(): Int {
|
||||
if (isEmpty()) throw IndexOutOfBoundsException()
|
||||
return front!!._val
|
||||
}
|
||||
|
||||
/* 連結リストを Array に変換して返す */
|
||||
fun toArray(): IntArray {
|
||||
var node = front
|
||||
val res = IntArray(size())
|
||||
for (i in res.indices) {
|
||||
res[i] = node!!._val
|
||||
node = node.next
|
||||
}
|
||||
return res
|
||||
}
|
||||
}
|
||||
|
||||
/* Driver Code */
|
||||
fun main() {
|
||||
/* キューを初期化 */
|
||||
val queue = LinkedListQueue()
|
||||
|
||||
/* 要素をエンキュー */
|
||||
queue.push(1)
|
||||
queue.push(3)
|
||||
queue.push(2)
|
||||
queue.push(5)
|
||||
queue.push(4)
|
||||
println("キュー queue = ${queue.toArray().contentToString()}")
|
||||
|
||||
/* キュー先頭の要素にアクセス */
|
||||
val peek = queue.peek()
|
||||
println("先頭要素 peek = $peek")
|
||||
|
||||
/* 要素をデキュー */
|
||||
val pop = queue.pop()
|
||||
println("デキューした要素 pop = $pop、デキュー後 queue = ${queue.toArray().contentToString()}")
|
||||
|
||||
/* キューの長さを取得 */
|
||||
val size = queue.size()
|
||||
println("キューの長さ size = $size")
|
||||
|
||||
/* キューが空かどうかを判定 */
|
||||
val isEmpty = queue.isEmpty()
|
||||
println("キューが空かどうか = $isEmpty")
|
||||
}
|
||||
@@ -0,0 +1,87 @@
|
||||
/**
|
||||
* File: linkedlist_stack.kt
|
||||
* Created Time: 2024-01-25
|
||||
* Author: curtishd (1023632660@qq.com)
|
||||
*/
|
||||
|
||||
package chapter_stack_and_queue
|
||||
|
||||
/* 連結リストベースのスタック */
|
||||
class LinkedListStack(
|
||||
private var stackPeek: ListNode? = null, // 先頭ノードをスタックトップとする
|
||||
private var stkSize: Int = 0 // スタックの長さ
|
||||
) {
|
||||
|
||||
/* スタックの長さを取得 */
|
||||
fun size(): Int {
|
||||
return stkSize
|
||||
}
|
||||
|
||||
/* スタックが空かどうかを判定 */
|
||||
fun isEmpty(): Boolean {
|
||||
return size() == 0
|
||||
}
|
||||
|
||||
/* プッシュ */
|
||||
fun push(num: Int) {
|
||||
val node = ListNode(num)
|
||||
node.next = stackPeek
|
||||
stackPeek = node
|
||||
stkSize++
|
||||
}
|
||||
|
||||
/* ポップ */
|
||||
fun pop(): Int? {
|
||||
val num = peek()
|
||||
stackPeek = stackPeek?.next
|
||||
stkSize--
|
||||
return num
|
||||
}
|
||||
|
||||
/* スタックトップの要素にアクセス */
|
||||
fun peek(): Int? {
|
||||
if (isEmpty()) throw IndexOutOfBoundsException()
|
||||
return stackPeek?._val
|
||||
}
|
||||
|
||||
/* List を Array に変換して返す */
|
||||
fun toArray(): IntArray {
|
||||
var node = stackPeek
|
||||
val res = IntArray(size())
|
||||
for (i in res.size - 1 downTo 0) {
|
||||
res[i] = node?._val!!
|
||||
node = node.next
|
||||
}
|
||||
return res
|
||||
}
|
||||
}
|
||||
|
||||
/* Driver Code */
|
||||
fun main() {
|
||||
/* スタックを初期化 */
|
||||
val stack = LinkedListStack()
|
||||
|
||||
/* 要素をプッシュ */
|
||||
stack.push(1)
|
||||
stack.push(3)
|
||||
stack.push(2)
|
||||
stack.push(5)
|
||||
stack.push(4)
|
||||
println("スタック stack = ${stack.toArray().contentToString()}")
|
||||
|
||||
/* スタックトップの要素にアクセス */
|
||||
val peek = stack.peek()!!
|
||||
println("スタックトップ要素 peek = $peek")
|
||||
|
||||
/* 要素をポップ */
|
||||
val pop = stack.pop()!!
|
||||
println("ポップした要素 pop = $pop、ポップ後の stack = ${stack.toArray().contentToString()}")
|
||||
|
||||
/* スタックの長さを取得 */
|
||||
val size = stack.size()
|
||||
println("スタックの長さ size = $size")
|
||||
|
||||
/* 空かどうかを判定 */
|
||||
val isEmpty = stack.isEmpty()
|
||||
println("スタックが空かどうか = $isEmpty")
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
/**
|
||||
* File: queue.kt
|
||||
* Created Time: 2024-01-25
|
||||
* Author: curtishd (1023632660@qq.com)
|
||||
*/
|
||||
|
||||
package chapter_stack_and_queue
|
||||
|
||||
import java.util.*
|
||||
|
||||
/* Driver Code */
|
||||
fun main() {
|
||||
/* キューを初期化 */
|
||||
val queue = LinkedList<Int>()
|
||||
|
||||
/* 要素をエンキュー */
|
||||
queue.offer(1)
|
||||
queue.offer(3)
|
||||
queue.offer(2)
|
||||
queue.offer(5)
|
||||
queue.offer(4)
|
||||
println("キュー queue = $queue")
|
||||
|
||||
/* キュー先頭の要素にアクセス */
|
||||
val peek = queue.peek()
|
||||
println("先頭要素 peek = $peek")
|
||||
|
||||
/* 要素をデキュー */
|
||||
val pop = queue.poll()
|
||||
println("デキューした要素 pop = $pop、デキュー後 queue = $queue")
|
||||
|
||||
/* キューの長さを取得 */
|
||||
val size = queue.size
|
||||
println("キューの長さ size = $size")
|
||||
|
||||
/* キューが空かどうかを判定 */
|
||||
val isEmpty = queue.isEmpty()
|
||||
println("キューが空かどうか = $isEmpty")
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
/**
|
||||
* File: stack.kt
|
||||
* Created Time: 2024-01-25
|
||||
* Author: curtishd (1023632660@qq.com)
|
||||
*/
|
||||
|
||||
package chapter_stack_and_queue
|
||||
|
||||
import java.util.*
|
||||
|
||||
/* Driver Code */
|
||||
fun main() {
|
||||
/* スタックを初期化 */
|
||||
val stack = Stack<Int>()
|
||||
|
||||
/* 要素をプッシュ */
|
||||
stack.push(1)
|
||||
stack.push(3)
|
||||
stack.push(2)
|
||||
stack.push(5)
|
||||
stack.push(4)
|
||||
println("スタック stack = $stack")
|
||||
|
||||
/* スタックトップの要素にアクセス */
|
||||
val peek = stack.peek()
|
||||
println("スタックトップ要素 peek = $peek")
|
||||
|
||||
/* 要素をポップ */
|
||||
val pop = stack.pop()
|
||||
println("ポップした要素 pop = $pop、ポップ後 stack = $stack")
|
||||
|
||||
/* スタックの長さを取得 */
|
||||
val size = stack.size
|
||||
println("スタックの長さ size = $size")
|
||||
|
||||
/* 空かどうかを判定 */
|
||||
val isEmpty = stack.isEmpty()
|
||||
println("スタックが空かどうか = $isEmpty")
|
||||
}
|
||||
@@ -0,0 +1,127 @@
|
||||
/**
|
||||
* File: array_binary_tree.kt
|
||||
* Created Time: 2024-01-25
|
||||
* Author: curtishd (1023632660@qq.com)
|
||||
*/
|
||||
|
||||
package chapter_tree
|
||||
|
||||
import utils.TreeNode
|
||||
import utils.printTree
|
||||
|
||||
/* 配列表現による二分木クラス */
|
||||
class ArrayBinaryTree(private val tree: MutableList<Int?>) {
|
||||
/* リスト容量 */
|
||||
fun size(): Int {
|
||||
return tree.size
|
||||
}
|
||||
|
||||
/* インデックス i のノードの値を取得 */
|
||||
fun _val(i: Int): Int? {
|
||||
// インデックスが範囲外なら、空きを表す null を返す
|
||||
if (i < 0 || i >= size()) return null
|
||||
return tree[i]
|
||||
}
|
||||
|
||||
/* インデックス i のノードの左子ノードのインデックスを取得 */
|
||||
fun left(i: Int): Int {
|
||||
return 2 * i + 1
|
||||
}
|
||||
|
||||
/* インデックス i のノードの右子ノードのインデックスを取得 */
|
||||
fun right(i: Int): Int {
|
||||
return 2 * i + 2
|
||||
}
|
||||
|
||||
/* インデックス i のノードの親ノードのインデックスを取得 */
|
||||
fun parent(i: Int): Int {
|
||||
return (i - 1) / 2
|
||||
}
|
||||
|
||||
/* レベル順走査 */
|
||||
fun levelOrder(): MutableList<Int?> {
|
||||
val res = mutableListOf<Int?>()
|
||||
// 配列を直接走査する
|
||||
for (i in 0..<size()) {
|
||||
if (_val(i) != null)
|
||||
res.add(_val(i))
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
/* 深さ優先探索 */
|
||||
fun dfs(i: Int, order: String, res: MutableList<Int?>) {
|
||||
// 空きスロットなら返す
|
||||
if (_val(i) == null)
|
||||
return
|
||||
// 先行順走査
|
||||
if ("pre" == order)
|
||||
res.add(_val(i))
|
||||
dfs(left(i), order, res)
|
||||
// 中順走査
|
||||
if ("in" == order)
|
||||
res.add(_val(i))
|
||||
dfs(right(i), order, res)
|
||||
// 後順走査
|
||||
if ("post" == order)
|
||||
res.add(_val(i))
|
||||
}
|
||||
|
||||
/* 先行順走査 */
|
||||
fun preOrder(): MutableList<Int?> {
|
||||
val res = mutableListOf<Int?>()
|
||||
dfs(0, "pre", res)
|
||||
return res
|
||||
}
|
||||
|
||||
/* 中順走査 */
|
||||
fun inOrder(): MutableList<Int?> {
|
||||
val res = mutableListOf<Int?>()
|
||||
dfs(0, "in", res)
|
||||
return res
|
||||
}
|
||||
|
||||
/* 後順走査 */
|
||||
fun postOrder(): MutableList<Int?> {
|
||||
val res = mutableListOf<Int?>()
|
||||
dfs(0, "post", res)
|
||||
return res
|
||||
}
|
||||
}
|
||||
|
||||
/* Driver Code */
|
||||
fun main() {
|
||||
// 二分木を初期化する
|
||||
// ここでは、リストから二分木を直接生成する関数を利用する
|
||||
val arr = mutableListOf(1, 2, 3, 4, null, 6, 7, 8, 9, null, null, 12, null, null, 15)
|
||||
|
||||
val root = TreeNode.listToTree(arr)
|
||||
println("\n二分木を初期化\n")
|
||||
println("二分木の配列表現:")
|
||||
println(arr)
|
||||
println("二分木の連結リスト表現:")
|
||||
printTree(root)
|
||||
|
||||
// 配列表現による二分木クラス
|
||||
val abt = ArrayBinaryTree(arr)
|
||||
|
||||
// ノードにアクセス
|
||||
val i = 1
|
||||
val l = abt.left(i)
|
||||
val r = abt.right(i)
|
||||
val p = abt.parent(i)
|
||||
println("現在のノードのインデックスは $i 、値は ${abt._val(i)}")
|
||||
println("左の子ノードのインデックスは $l 、値は ${abt._val(l)}")
|
||||
println("右の子ノードのインデックスは $r 、値は ${abt._val(r)}")
|
||||
println("親ノードのインデックスは $p 、値は ${abt._val(p)}")
|
||||
|
||||
// 木を走査
|
||||
var res = abt.levelOrder()
|
||||
println("\nレベル順走査:$res")
|
||||
res = abt.preOrder()
|
||||
println("先行順走査:$res")
|
||||
res = abt.inOrder()
|
||||
println("中間順走査:$res")
|
||||
res = abt.postOrder()
|
||||
println("後行順走査:$res")
|
||||
}
|
||||
@@ -0,0 +1,223 @@
|
||||
/**
|
||||
* File: avl_tree.kt
|
||||
* Created Time: 2024-01-25
|
||||
* Author: curtishd (1023632660@qq.com)
|
||||
*/
|
||||
|
||||
package chapter_tree
|
||||
|
||||
import utils.TreeNode
|
||||
import utils.printTree
|
||||
import kotlin.math.max
|
||||
|
||||
/* AVL 木 */
|
||||
class AVLTree {
|
||||
var root: TreeNode? = null // 根ノード
|
||||
|
||||
/* ノードの高さを取得 */
|
||||
fun height(node: TreeNode?): Int {
|
||||
// 空ノードの高さは -1、葉ノードの高さは 0
|
||||
return node?.height ?: -1
|
||||
}
|
||||
|
||||
/* ノードの高さを更新する */
|
||||
private fun updateHeight(node: TreeNode?) {
|
||||
// ノードの高さは最も高い部分木の高さ + 1 に等しい
|
||||
node?.height = max(height(node?.left), height(node?.right)) + 1
|
||||
}
|
||||
|
||||
/* 平衡係数を取得 */
|
||||
fun balanceFactor(node: TreeNode?): Int {
|
||||
// 空ノードの平衡係数は 0
|
||||
if (node == null) return 0
|
||||
// ノードの平衡係数 = 左部分木の高さ - 右部分木の高さ
|
||||
return height(node.left) - height(node.right)
|
||||
}
|
||||
|
||||
/* 右回転 */
|
||||
private fun rightRotate(node: TreeNode?): TreeNode {
|
||||
val child = node!!.left
|
||||
val grandChild = child!!.right
|
||||
// child を支点として node を右回転させる
|
||||
child.right = node
|
||||
node.left = grandChild
|
||||
// ノードの高さを更新する
|
||||
updateHeight(node)
|
||||
updateHeight(child)
|
||||
// 回転後の部分木の根ノードを返す
|
||||
return child
|
||||
}
|
||||
|
||||
/* 左回転 */
|
||||
private fun leftRotate(node: TreeNode?): TreeNode {
|
||||
val child = node!!.right
|
||||
val grandChild = child!!.left
|
||||
// child を支点として node を左回転させる
|
||||
child.left = node
|
||||
node.right = grandChild
|
||||
// ノードの高さを更新する
|
||||
updateHeight(node)
|
||||
updateHeight(child)
|
||||
// 回転後の部分木の根ノードを返す
|
||||
return child
|
||||
}
|
||||
|
||||
/* 回転操作を行い、この部分木の平衡を回復する */
|
||||
private fun rotate(node: TreeNode): TreeNode {
|
||||
// ノード node の平衡係数を取得
|
||||
val balanceFactor = balanceFactor(node)
|
||||
// 左に偏った木
|
||||
if (balanceFactor > 1) {
|
||||
if (balanceFactor(node.left) >= 0) {
|
||||
// 右回転
|
||||
return rightRotate(node)
|
||||
} else {
|
||||
// 左回転してから右回転
|
||||
node.left = leftRotate(node.left)
|
||||
return rightRotate(node)
|
||||
}
|
||||
}
|
||||
// 右に偏った木
|
||||
if (balanceFactor < -1) {
|
||||
if (balanceFactor(node.right) <= 0) {
|
||||
// 左回転
|
||||
return leftRotate(node)
|
||||
} else {
|
||||
// 右回転してから左回転
|
||||
node.right = rightRotate(node.right)
|
||||
return leftRotate(node)
|
||||
}
|
||||
}
|
||||
// 平衡木なので回転不要、そのまま返す
|
||||
return node
|
||||
}
|
||||
|
||||
/* ノードを挿入 */
|
||||
fun insert(_val: Int) {
|
||||
root = insertHelper(root, _val)
|
||||
}
|
||||
|
||||
/* ノードを再帰的に挿入する(補助メソッド) */
|
||||
private fun insertHelper(n: TreeNode?, _val: Int): TreeNode {
|
||||
if (n == null)
|
||||
return TreeNode(_val)
|
||||
var node = n
|
||||
/* 1. 挿入位置を探索してノードを挿入 */
|
||||
if (_val < node._val)
|
||||
node.left = insertHelper(node.left, _val)
|
||||
else if (_val > node._val)
|
||||
node.right = insertHelper(node.right, _val)
|
||||
else
|
||||
return node // 重複ノードは挿入せず、そのまま返す
|
||||
updateHeight(node) // ノードの高さを更新する
|
||||
/* 2. 回転操作を行い、部分木の平衡を回復する */
|
||||
node = rotate(node)
|
||||
// 部分木の根ノードを返す
|
||||
return node
|
||||
}
|
||||
|
||||
/* ノードを削除 */
|
||||
fun remove(_val: Int) {
|
||||
root = removeHelper(root, _val)
|
||||
}
|
||||
|
||||
/* ノードを再帰的に削除する(補助メソッド) */
|
||||
private fun removeHelper(n: TreeNode?, _val: Int): TreeNode? {
|
||||
var node = n ?: return null
|
||||
/* 1. ノードを探索して削除 */
|
||||
if (_val < node._val)
|
||||
node.left = removeHelper(node.left, _val)
|
||||
else if (_val > node._val)
|
||||
node.right = removeHelper(node.right, _val)
|
||||
else {
|
||||
if (node.left == null || node.right == null) {
|
||||
val child = if (node.left != null)
|
||||
node.left
|
||||
else
|
||||
node.right
|
||||
// 子ノード数 = 0 の場合、node をそのまま削除して返す
|
||||
if (child == null)
|
||||
return null
|
||||
// 子ノード数 = 1 の場合、node をそのまま削除する
|
||||
else
|
||||
node = child
|
||||
} else {
|
||||
// 子ノード数 = 2 の場合、中順走査の次のノードを削除し、そのノードで現在のノードを置き換える
|
||||
var temp = node.right
|
||||
while (temp!!.left != null) {
|
||||
temp = temp.left
|
||||
}
|
||||
node.right = removeHelper(node.right, temp._val)
|
||||
node._val = temp._val
|
||||
}
|
||||
}
|
||||
updateHeight(node) // ノードの高さを更新する
|
||||
/* 2. 回転操作を行い、部分木の平衡を回復する */
|
||||
node = rotate(node)
|
||||
// 部分木の根ノードを返す
|
||||
return node
|
||||
}
|
||||
|
||||
/* ノードを探索 */
|
||||
fun search(_val: Int): TreeNode? {
|
||||
var cur = root
|
||||
// ループで探索し、葉ノードを越えたら抜ける
|
||||
while (cur != null) {
|
||||
// 目標ノードは cur の右部分木にある
|
||||
cur = if (cur._val < _val)
|
||||
cur.right!!
|
||||
// 目標ノードは cur の左部分木にある
|
||||
else if (cur._val > _val)
|
||||
cur.left
|
||||
// 目標ノードが見つかったらループを抜ける
|
||||
else
|
||||
break
|
||||
}
|
||||
// 目標ノードを返す
|
||||
return cur
|
||||
}
|
||||
}
|
||||
|
||||
fun testInsert(tree: AVLTree, _val: Int) {
|
||||
tree.insert(_val)
|
||||
println("\nノード $_val を挿入後、AVL 木は")
|
||||
printTree(tree.root)
|
||||
}
|
||||
|
||||
fun testRemove(tree: AVLTree, _val: Int) {
|
||||
tree.remove(_val)
|
||||
println("\nノード $_val を削除後、AVL 木は")
|
||||
printTree(tree.root)
|
||||
}
|
||||
|
||||
/* Driver Code */
|
||||
fun main() {
|
||||
/* 空の AVL 木を初期化する */
|
||||
val avlTree = AVLTree()
|
||||
|
||||
/* ノードを挿入 */
|
||||
// ノード挿入後に AVL 木がどのように平衡を保つかに注目してほしい
|
||||
testInsert(avlTree, 1)
|
||||
testInsert(avlTree, 2)
|
||||
testInsert(avlTree, 3)
|
||||
testInsert(avlTree, 4)
|
||||
testInsert(avlTree, 5)
|
||||
testInsert(avlTree, 8)
|
||||
testInsert(avlTree, 7)
|
||||
testInsert(avlTree, 9)
|
||||
testInsert(avlTree, 10)
|
||||
testInsert(avlTree, 6)
|
||||
|
||||
/* 重複ノードを挿入する */
|
||||
testInsert(avlTree, 7)
|
||||
|
||||
/* ノードを削除 */
|
||||
// ノード削除後に AVL 木がどのように平衡を保つかに注目してほしい
|
||||
testRemove(avlTree, 8) // 次数 0 のノードを削除する
|
||||
testRemove(avlTree, 5) // 次数 1 のノードを削除する
|
||||
testRemove(avlTree, 4) // 次数 2 のノードを削除する
|
||||
|
||||
/* ノードを検索 */
|
||||
val node = avlTree.search(7)
|
||||
println("\n 見つかったノードオブジェクトは $node、ノードの値 = ${node?._val}")
|
||||
}
|
||||
@@ -0,0 +1,157 @@
|
||||
/**
|
||||
* File: binary_search_tree.kt
|
||||
* Created Time: 2024-01-25
|
||||
* Author: curtishd (1023632660@qq.com)
|
||||
*/
|
||||
|
||||
package chapter_tree
|
||||
|
||||
import utils.TreeNode
|
||||
import utils.printTree
|
||||
|
||||
/* 二分探索木 */
|
||||
class BinarySearchTree {
|
||||
// 空の木を初期化する
|
||||
private var root: TreeNode? = null
|
||||
|
||||
/* 二分木の根ノードを取得 */
|
||||
fun getRoot(): TreeNode? {
|
||||
return root
|
||||
}
|
||||
|
||||
/* ノードを探索 */
|
||||
fun search(num: Int): TreeNode? {
|
||||
var cur = root
|
||||
// ループで探索し、葉ノードを越えたら抜ける
|
||||
while (cur != null) {
|
||||
// 目標ノードは cur の右部分木にある
|
||||
cur = if (cur._val < num)
|
||||
cur.right
|
||||
// 目標ノードは cur の左部分木にある
|
||||
else if (cur._val > num)
|
||||
cur.left
|
||||
// 目標ノードが見つかったらループを抜ける
|
||||
else
|
||||
break
|
||||
}
|
||||
// 目標ノードを返す
|
||||
return cur
|
||||
}
|
||||
|
||||
/* ノードを挿入 */
|
||||
fun insert(num: Int) {
|
||||
// 木が空なら、根ノードを初期化する
|
||||
if (root == null) {
|
||||
root = TreeNode(num)
|
||||
return
|
||||
}
|
||||
var cur = root
|
||||
var pre: TreeNode? = null
|
||||
// ループで探索し、葉ノードを越えたら抜ける
|
||||
while (cur != null) {
|
||||
// 重複ノードが見つかったら、直ちに返す
|
||||
if (cur._val == num)
|
||||
return
|
||||
pre = cur
|
||||
// 挿入位置は cur の右部分木にある
|
||||
cur = if (cur._val < num)
|
||||
cur.right
|
||||
// 挿入位置は cur の左部分木にある
|
||||
else
|
||||
cur.left
|
||||
}
|
||||
// ノードを挿入
|
||||
val node = TreeNode(num)
|
||||
if (pre?._val!! < num)
|
||||
pre.right = node
|
||||
else
|
||||
pre.left = node
|
||||
}
|
||||
|
||||
/* ノードを削除 */
|
||||
fun remove(num: Int) {
|
||||
// 木が空なら、そのまま早期リターンする
|
||||
if (root == null)
|
||||
return
|
||||
var cur = root
|
||||
var pre: TreeNode? = null
|
||||
// ループで探索し、葉ノードを越えたら抜ける
|
||||
while (cur != null) {
|
||||
// 削除対象のノードが見つかったら、ループを抜ける
|
||||
if (cur._val == num)
|
||||
break
|
||||
pre = cur
|
||||
// 削除対象ノードは cur の右部分木にある
|
||||
cur = if (cur._val < num)
|
||||
cur.right
|
||||
// 削除対象ノードは cur の左部分木にある
|
||||
else
|
||||
cur.left
|
||||
}
|
||||
// 削除対象ノードがなければそのまま返す
|
||||
if (cur == null)
|
||||
return
|
||||
// 子ノード数 = 0 or 1
|
||||
if (cur.left == null || cur.right == null) {
|
||||
// 子ノード数が 0 / 1 のとき、child = null / その子ノード
|
||||
val child = if (cur.left != null)
|
||||
cur.left
|
||||
else
|
||||
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 != null) {
|
||||
tmp = tmp.left
|
||||
}
|
||||
// ノード tmp を再帰的に削除
|
||||
remove(tmp._val)
|
||||
// tmp で cur を上書きする
|
||||
cur._val = tmp._val
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Driver Code */
|
||||
fun main() {
|
||||
/* 二分探索木を初期化 */
|
||||
val bst = BinarySearchTree()
|
||||
// 注意:挿入順序が異なると異なる二分木が生成される。このシーケンスからは完全二分木を生成できる
|
||||
val nums = intArrayOf(8, 4, 12, 2, 6, 10, 14, 1, 3, 5, 7, 9, 11, 13, 15)
|
||||
for (num in nums) {
|
||||
bst.insert(num)
|
||||
}
|
||||
println("\n初期化した二分木は\n")
|
||||
printTree(bst.getRoot())
|
||||
|
||||
/* ノードを探索 */
|
||||
val node = bst.search(7)
|
||||
println("見つかったノードオブジェクトは $node、ノードの値 = ${node?._val}")
|
||||
|
||||
/* ノードを挿入 */
|
||||
bst.insert(16)
|
||||
println("\nノード 16 を挿入後、二分木は\n")
|
||||
printTree(bst.getRoot())
|
||||
|
||||
/* ノードを削除 */
|
||||
bst.remove(1)
|
||||
println("\nノード 1 を削除後、二分木は\n")
|
||||
printTree(bst.getRoot())
|
||||
bst.remove(2)
|
||||
println("\nノード 2 を削除後、二分木は\n")
|
||||
printTree(bst.getRoot())
|
||||
bst.remove(4)
|
||||
println("\nノード 4 を削除後、二分木は\n")
|
||||
printTree(bst.getRoot())
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
/**
|
||||
* File: binary_tree.kt
|
||||
* Created Time: 2024-01-25
|
||||
* Author: curtishd (1023632660@qq.com)
|
||||
*/
|
||||
|
||||
package chapter_tree
|
||||
|
||||
import utils.TreeNode
|
||||
import utils.printTree
|
||||
|
||||
/* Driver Code */
|
||||
fun main() {
|
||||
/* 二分木を初期化 */
|
||||
// ノードを初期化
|
||||
val n1 = TreeNode(1)
|
||||
val n2 = TreeNode(2)
|
||||
val n3 = TreeNode(3)
|
||||
val n4 = TreeNode(4)
|
||||
val n5 = TreeNode(5)
|
||||
// ノード間の参照(ポインタ)を構築する
|
||||
n1.left = n2
|
||||
n1.right = n3
|
||||
n2.left = n4
|
||||
n2.right = n5
|
||||
println("\n二分木を初期化\n")
|
||||
printTree(n1)
|
||||
|
||||
/* ノードの挿入と削除 */
|
||||
val P = TreeNode(0)
|
||||
// n1 -> n2 の間にノード P を挿入
|
||||
n1.left = P
|
||||
P.left = n2
|
||||
println("\nノード P を挿入後\n")
|
||||
printTree(n1)
|
||||
// ノード P を削除
|
||||
n1.left = n2
|
||||
println("\nノード P を削除後\n")
|
||||
printTree(n1)
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
/**
|
||||
* File: binary_tree_bfs.kt
|
||||
* Created Time: 2024-01-25
|
||||
* Author: curtishd (1023632660@qq.com)
|
||||
*/
|
||||
|
||||
package chapter_tree
|
||||
|
||||
import utils.TreeNode
|
||||
import utils.printTree
|
||||
import java.util.*
|
||||
|
||||
/* レベル順走査 */
|
||||
fun levelOrder(root: TreeNode?): MutableList<Int> {
|
||||
// キューを初期化し、ルートノードを追加する
|
||||
val queue = LinkedList<TreeNode?>()
|
||||
queue.add(root)
|
||||
// 走査順序を保存するためのリストを初期化する
|
||||
val list = mutableListOf<Int>()
|
||||
while (queue.isNotEmpty()) {
|
||||
val node = queue.poll() // デキュー
|
||||
list.add(node?._val!!) // ノードの値を保存する
|
||||
if (node.left != null)
|
||||
queue.offer(node.left) // 左子ノードをキューに追加
|
||||
if (node.right != null)
|
||||
queue.offer(node.right) // 右子ノードをキューに追加
|
||||
}
|
||||
return list
|
||||
}
|
||||
|
||||
/* Driver Code */
|
||||
fun main() {
|
||||
/* 二分木を初期化 */
|
||||
// ここではリストから直接二分木を生成する関数を利用する
|
||||
val root = TreeNode.listToTree(mutableListOf(1, 2, 3, 4, 5, 6, 7))
|
||||
println("\n二分木を初期化\n")
|
||||
printTree(root)
|
||||
|
||||
/* レベル順走査 */
|
||||
val list = levelOrder(root)
|
||||
println("\nレベル順走査のノード出力順 = $list")
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
/**
|
||||
* File: binary_tree_dfs.kt
|
||||
* Created Time: 2024-01-25
|
||||
* Author: curtishd (1023632660@qq.com)
|
||||
*/
|
||||
|
||||
package chapter_tree
|
||||
|
||||
import utils.TreeNode
|
||||
import utils.printTree
|
||||
|
||||
// 走査順序を格納するリストを初期化
|
||||
var list = mutableListOf<Int>()
|
||||
|
||||
/* 先行順走査 */
|
||||
fun preOrder(root: TreeNode?) {
|
||||
if (root == null) return
|
||||
// 訪問順序:根ノード -> 左部分木 -> 右部分木
|
||||
list.add(root._val)
|
||||
preOrder(root.left)
|
||||
preOrder(root.right)
|
||||
}
|
||||
|
||||
/* 中順走査 */
|
||||
fun inOrder(root: TreeNode?) {
|
||||
if (root == null) return
|
||||
// 訪問優先順: 左部分木 -> 根ノード -> 右部分木
|
||||
inOrder(root.left)
|
||||
list.add(root._val)
|
||||
inOrder(root.right)
|
||||
}
|
||||
|
||||
/* 後順走査 */
|
||||
fun postOrder(root: TreeNode?) {
|
||||
if (root == null) return
|
||||
// 訪問優先順: 左部分木 -> 右部分木 -> 根ノード
|
||||
postOrder(root.left)
|
||||
postOrder(root.right)
|
||||
list.add(root._val)
|
||||
}
|
||||
|
||||
/* Driver Code */
|
||||
fun main() {
|
||||
/* 二分木を初期化 */
|
||||
// ここではリストから直接二分木を生成する関数を利用する
|
||||
val root = TreeNode.listToTree(mutableListOf(1, 2, 3, 4, 5, 6, 7))
|
||||
println("\n二分木を初期化\n")
|
||||
printTree(root)
|
||||
|
||||
/* 先行順走査 */
|
||||
list.clear()
|
||||
preOrder(root)
|
||||
println("\n先行順走査のノード出力順 = $list")
|
||||
|
||||
/* 中順走査 */
|
||||
list.clear()
|
||||
inOrder(root)
|
||||
println("\n中間順走査のノード出力順 = $list")
|
||||
|
||||
/* 後順走査 */
|
||||
list.clear()
|
||||
postOrder(root)
|
||||
println("\n後行順走査のノード出力順 = $list")
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
/**
|
||||
* File: ListNode.kt
|
||||
* Created Time: 2024-01-25
|
||||
* Author: curtishd (1023632660@qq.com)
|
||||
*/
|
||||
|
||||
package utils
|
||||
|
||||
/* 連結リストノード */
|
||||
class ListNode(var _val: Int) {
|
||||
var next: ListNode? = null
|
||||
|
||||
companion object {
|
||||
/* リストを連結リストにデシリアライズする */
|
||||
fun arrToLinkedList(arr: IntArray): ListNode? {
|
||||
val dum = ListNode(0)
|
||||
var head = dum
|
||||
for (_val in arr) {
|
||||
head.next = ListNode(_val)
|
||||
head = head.next!!
|
||||
}
|
||||
return dum.next
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,107 @@
|
||||
/**
|
||||
* File: PrintUtil.kt
|
||||
* Created Time: 2024-01-25
|
||||
* Author: curtishd (1023632660@qq.com)
|
||||
*/
|
||||
|
||||
package utils
|
||||
|
||||
import java.util.*
|
||||
|
||||
class Trunk(var prev: Trunk?, var str: String)
|
||||
|
||||
/* 行列を出力する(Array) */
|
||||
fun <T> printMatrix(matrix: Array<Array<T>>) {
|
||||
println("[")
|
||||
for (row in matrix) {
|
||||
println(" $row,")
|
||||
}
|
||||
println("]")
|
||||
}
|
||||
|
||||
/* 行列を出力する(List) */
|
||||
fun <T> printMatrix(matrix: MutableList<MutableList<T>>) {
|
||||
println("[")
|
||||
for (row in matrix) {
|
||||
println(" $row,")
|
||||
}
|
||||
println("]")
|
||||
}
|
||||
|
||||
/* 連結リストを出力 */
|
||||
fun printLinkedList(h: ListNode?) {
|
||||
var head = h
|
||||
val list = mutableListOf<String>()
|
||||
while (head != null) {
|
||||
list.add(head._val.toString())
|
||||
head = head.next
|
||||
}
|
||||
println(list.joinToString(separator = " -> "))
|
||||
}
|
||||
|
||||
/* 二分木を出力 */
|
||||
fun printTree(root: TreeNode?) {
|
||||
printTree(root, null, false)
|
||||
}
|
||||
|
||||
/**
|
||||
* 二分木を出力
|
||||
* This tree printer is borrowed from TECHIE DELIGHT
|
||||
* https://www.techiedelight.com/c-program-print-binary-tree/
|
||||
*/
|
||||
fun printTree(root: TreeNode?, prev: Trunk?, isRight: Boolean) {
|
||||
if (root == null) {
|
||||
return
|
||||
}
|
||||
|
||||
var prevStr = " "
|
||||
val trunk = Trunk(prev, prevStr)
|
||||
|
||||
printTree(root.right, trunk, true)
|
||||
|
||||
if (prev == null) {
|
||||
trunk.str = "———"
|
||||
} else if (isRight) {
|
||||
trunk.str = "/———"
|
||||
prevStr = " |"
|
||||
} else {
|
||||
trunk.str = "\\———"
|
||||
prev.str = prevStr
|
||||
}
|
||||
|
||||
showTrunks(trunk)
|
||||
println(" ${root._val}")
|
||||
|
||||
if (prev != null) {
|
||||
prev.str = prevStr
|
||||
}
|
||||
trunk.str = " |"
|
||||
|
||||
printTree(root.left, trunk, false)
|
||||
}
|
||||
|
||||
fun showTrunks(p: Trunk?) {
|
||||
if (p == null) {
|
||||
return
|
||||
}
|
||||
showTrunks(p.prev)
|
||||
print(p.str)
|
||||
}
|
||||
|
||||
/* ハッシュテーブルを出力 */
|
||||
fun <K, V> printHashMap(map: Map<K, V>) {
|
||||
for ((key, value) in map) {
|
||||
println("${key.toString()} -> $value")
|
||||
}
|
||||
}
|
||||
|
||||
/* ヒープを出力 */
|
||||
fun printHeap(queue: Queue<Int>?) {
|
||||
val list = mutableListOf<Int?>()
|
||||
queue?.let { list.addAll(it) }
|
||||
print("ヒープの配列表現:")
|
||||
println(list)
|
||||
println("ヒープの木構造表現:")
|
||||
val root = TreeNode.listToTree(list)
|
||||
printTree(root)
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
/**
|
||||
* File: TreeNode.kt
|
||||
* Created Time: 2024-01-25
|
||||
* Author: curtishd (1023632660@qq.com)
|
||||
*/
|
||||
|
||||
package utils
|
||||
|
||||
/* 二分木ノードクラス */
|
||||
/* コンストラクタ */
|
||||
class TreeNode(
|
||||
var _val: Int // ノード値
|
||||
) {
|
||||
var height: Int = 0 // ノードの高さ
|
||||
var left: TreeNode? = null // 左子ノードへの参照
|
||||
var right: TreeNode? = null // 右子ノードへの参照
|
||||
|
||||
// シリアライズの符号化規則は以下を参照:
|
||||
// https://www.hello-algo.com/chapter_tree/array_representation_of_tree/
|
||||
// 二分木の配列表現:
|
||||
// [1, 2, 3, 4, None, 6, 7, 8, 9, None, None, 12, None, None, 15]
|
||||
// 二分木の連結リスト表現:
|
||||
// /——— 15
|
||||
// /——— 7
|
||||
// /——— 3
|
||||
// | \——— 6
|
||||
// | \——— 12
|
||||
// ——— 1
|
||||
// \——— 2
|
||||
// | /——— 9
|
||||
// \——— 4
|
||||
// \——— 8
|
||||
|
||||
/* リストを二分木にデシリアライズする: 再帰 */
|
||||
companion object {
|
||||
private fun listToTreeDFS(arr: MutableList<Int?>, i: Int): TreeNode? {
|
||||
if (i < 0 || i >= arr.size || arr[i] == null) {
|
||||
return null
|
||||
}
|
||||
val root = TreeNode(arr[i]!!)
|
||||
root.left = listToTreeDFS(arr, 2 * i + 1)
|
||||
root.right = listToTreeDFS(arr, 2 * i + 2)
|
||||
return root
|
||||
}
|
||||
|
||||
/* リストを二分木にデシリアライズする */
|
||||
fun listToTree(arr: MutableList<Int?>): TreeNode? {
|
||||
return listToTreeDFS(arr, 0)
|
||||
}
|
||||
|
||||
/* 二分木をリストにシリアライズする: 再帰 */
|
||||
private fun treeToListDFS(root: TreeNode?, i: Int, res: MutableList<Int?>) {
|
||||
if (root == null) return
|
||||
while (i >= res.size) {
|
||||
res.add(null)
|
||||
}
|
||||
res[i] = root._val
|
||||
treeToListDFS(root.left, 2 * i + 1, res)
|
||||
treeToListDFS(root.right, 2 * i + 2, res)
|
||||
}
|
||||
|
||||
/* 二分木をリストにシリアライズする */
|
||||
fun treeToList(root: TreeNode?): MutableList<Int?> {
|
||||
val res = mutableListOf<Int?>()
|
||||
treeToListDFS(root, 0, res)
|
||||
return res
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
/**
|
||||
* File: Vertex.kt
|
||||
* Created Time: 2024-01-25
|
||||
* Author: curtishd (1023632660@qq.com)
|
||||
*/
|
||||
|
||||
package utils
|
||||
|
||||
/* 頂点クラス */
|
||||
class Vertex(val _val: Int) {
|
||||
companion object {
|
||||
/* 値リスト vals を入力し、頂点リスト vets を返す */
|
||||
fun valsToVets(vals: IntArray): Array<Vertex?> {
|
||||
val vets = arrayOfNulls<Vertex>(vals.size)
|
||||
for (i in vals.indices) {
|
||||
vets[i] = Vertex(vals[i])
|
||||
}
|
||||
return vets
|
||||
}
|
||||
|
||||
/* 頂点リスト vets を入力し、値リスト vals を返す */
|
||||
fun vetsToVals(vets: MutableList<Vertex?>): MutableList<Int> {
|
||||
val vals = mutableListOf<Int>()
|
||||
for (vet in vets) {
|
||||
vals.add(vet!!._val)
|
||||
}
|
||||
return vals
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user