mirror of
https://github.com/krahets/hello-algo.git
synced 2026-09-19 21:17:15 +00:00
Translate all code to English (#1836)
* Review the EN heading format. * Fix pythontutor headings. * Fix pythontutor headings. * bug fixes * Fix headings in **/summary.md * Revisit the CN-to-EN translation for Python code using Claude-4.5 * Revisit the CN-to-EN translation for Java code using Claude-4.5 * Revisit the CN-to-EN translation for Cpp code using Claude-4.5. * Fix the dictionary. * Fix cpp code translation for the multipart strings. * Translate Go code to English. * Update workflows to test EN code. * Add EN translation for C. * Add EN translation for CSharp. * Add EN translation for Swift. * Trigger the CI check. * Revert. * Update en/hash_map.md * Add the EN version of Dart code. * Add the EN version of Kotlin code. * Add missing code files. * Add the EN version of JavaScript code. * Add the EN version of TypeScript code. * Fix the workflows. * Add the EN version of Ruby code. * Add the EN version of Rust code. * Update the CI check for the English version code. * Update Python CI check. * Fix cmakelists for en/C code. * Fix Ruby comments
This commit is contained in:
@@ -0,0 +1,102 @@
|
||||
/**
|
||||
* File: array.kt
|
||||
* Created Time: 2024-01-25
|
||||
* Author: curtishd (1023632660@qq.com)
|
||||
*/
|
||||
|
||||
package chapter_array_and_linkedlist
|
||||
|
||||
import java.util.concurrent.ThreadLocalRandom
|
||||
|
||||
/* Random access to element */
|
||||
fun randomAccess(nums: IntArray): Int {
|
||||
// Randomly select a number in interval [0, nums.size)
|
||||
val randomIndex = ThreadLocalRandom.current().nextInt(0, nums.size)
|
||||
// Retrieve and return the random element
|
||||
val randomNum = nums[randomIndex]
|
||||
return randomNum
|
||||
}
|
||||
|
||||
/* Extend array length */
|
||||
fun extend(nums: IntArray, enlarge: Int): IntArray {
|
||||
// Initialize an array with extended length
|
||||
val res = IntArray(nums.size + enlarge)
|
||||
// Copy all elements from the original array to the new array
|
||||
for (i in nums.indices) {
|
||||
res[i] = nums[i]
|
||||
}
|
||||
// Return the extended new array
|
||||
return res
|
||||
}
|
||||
|
||||
/* Insert element num at index index in the array */
|
||||
fun insert(nums: IntArray, num: Int, index: Int) {
|
||||
// Move all elements at and after index index backward by one position
|
||||
for (i in nums.size - 1 downTo index + 1) {
|
||||
nums[i] = nums[i - 1]
|
||||
}
|
||||
// Assign num to the element at index index
|
||||
nums[index] = num
|
||||
}
|
||||
|
||||
/* Remove the element at index index */
|
||||
fun remove(nums: IntArray, index: Int) {
|
||||
// Move all elements after index index forward by one position
|
||||
for (i in index..<nums.size - 1) {
|
||||
nums[i] = nums[i + 1]
|
||||
}
|
||||
}
|
||||
|
||||
/* Traverse array */
|
||||
fun traverse(nums: IntArray) {
|
||||
var count = 0
|
||||
// Traverse array by index
|
||||
for (i in nums.indices) {
|
||||
count += nums[i]
|
||||
}
|
||||
// Direct traversal of array elements
|
||||
for (j in nums) {
|
||||
count += j
|
||||
}
|
||||
}
|
||||
|
||||
/* Find the specified element in the array */
|
||||
fun find(nums: IntArray, target: Int): Int {
|
||||
for (i in nums.indices) {
|
||||
if (nums[i] == target)
|
||||
return i
|
||||
}
|
||||
return -1
|
||||
}
|
||||
|
||||
/* Driver Code */
|
||||
fun main() {
|
||||
/* Initialize array */
|
||||
val arr = IntArray(5)
|
||||
println("Array arr = ${arr.contentToString()}")
|
||||
var nums = intArrayOf(1, 3, 2, 5, 4)
|
||||
println("Array nums = ${nums.contentToString()}")
|
||||
|
||||
/* Insert element */
|
||||
val randomNum: Int = randomAccess(nums)
|
||||
println("Get random element $randomNum from nums")
|
||||
|
||||
/* Traverse array */
|
||||
nums = extend(nums, 3)
|
||||
println("Extend array length to 8, get nums = ${nums.contentToString()}")
|
||||
|
||||
/* Insert element */
|
||||
insert(nums, 6, 3)
|
||||
println("Insert number 6 at index 3, get nums = ${nums.contentToString()}")
|
||||
|
||||
/* Remove element */
|
||||
remove(nums, 2)
|
||||
println("Delete element at index 2, get nums = ${nums.contentToString()}")
|
||||
|
||||
/* Traverse array */
|
||||
traverse(nums)
|
||||
|
||||
/* Find element */
|
||||
val index: Int = find(nums, 3)
|
||||
println("Find element 3 in nums, index = $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
|
||||
|
||||
/* Insert node P after node n0 in the linked list */
|
||||
fun insert(n0: ListNode?, p: ListNode?) {
|
||||
val n1 = n0?.next
|
||||
p?.next = n1
|
||||
n0?.next = p
|
||||
}
|
||||
|
||||
/* Remove the first node after node n0 in the linked list */
|
||||
fun remove(n0: ListNode?) {
|
||||
if (n0?.next == null)
|
||||
return
|
||||
// n0 -> P -> n1
|
||||
val p = n0.next
|
||||
val n1 = p?.next
|
||||
n0.next = n1
|
||||
}
|
||||
|
||||
/* Access the node at index index in the linked list */
|
||||
fun access(head: ListNode?, index: Int): ListNode? {
|
||||
var h = head
|
||||
for (i in 0..<index) {
|
||||
if (h == null)
|
||||
return null
|
||||
h = h.next
|
||||
}
|
||||
return h
|
||||
}
|
||||
|
||||
/* Find the first node with value target in the linked list */
|
||||
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() {
|
||||
/* Initialize linked list */
|
||||
// Initialize each node
|
||||
val n0 = ListNode(1)
|
||||
val n1 = ListNode(3)
|
||||
val n2 = ListNode(2)
|
||||
val n3 = ListNode(5)
|
||||
val n4 = ListNode(4)
|
||||
|
||||
// Build references between nodes
|
||||
n0.next = n1
|
||||
n1.next = n2
|
||||
n2.next = n3
|
||||
n3.next = n4
|
||||
println("Initialized linked list is")
|
||||
printLinkedList(n0)
|
||||
|
||||
/* Insert node */
|
||||
insert(n0, ListNode(0))
|
||||
println("Linked list after inserting node is")
|
||||
printLinkedList(n0)
|
||||
|
||||
/* Remove node */
|
||||
remove(n0)
|
||||
println("Linked list after removing node is")
|
||||
printLinkedList(n0)
|
||||
|
||||
/* Access node */
|
||||
val node = access(n0, 3)!!
|
||||
println("Value of node at index 3 in linked list = ${node._val}")
|
||||
|
||||
/* Search node */
|
||||
val index = find(n0, 2)
|
||||
println("Index of node with value 2 in linked list = $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() {
|
||||
/* Initialize list */
|
||||
// Mutable collection
|
||||
val nums = mutableListOf(1, 3, 2, 5, 4)
|
||||
println("List nums = $nums")
|
||||
|
||||
/* Update element */
|
||||
val num = nums[1]
|
||||
println("Access element at index 1, get num = $num")
|
||||
|
||||
/* Add elements at the end */
|
||||
nums[1] = 0
|
||||
println("Update element at index 1 to 0, get nums = $nums")
|
||||
|
||||
/* Remove element */
|
||||
nums.clear()
|
||||
println("After clearing list, nums = $nums")
|
||||
|
||||
/* Direct traversal of list elements */
|
||||
nums.add(1)
|
||||
nums.add(3)
|
||||
nums.add(2)
|
||||
nums.add(5)
|
||||
nums.add(4)
|
||||
println("After adding elements, nums = $nums")
|
||||
|
||||
/* Sort list */
|
||||
nums.add(3, 6)
|
||||
println("Insert number 6 at index 3, get nums = $nums")
|
||||
|
||||
/* Remove element */
|
||||
nums.removeAt(3)
|
||||
println("Delete element at index 3, get nums = $nums")
|
||||
|
||||
/* Traverse list by index */
|
||||
var count = 0
|
||||
for (i in nums.indices) {
|
||||
count += nums[i]
|
||||
}
|
||||
|
||||
/* Directly traverse list elements */
|
||||
for (j in nums) {
|
||||
count += j
|
||||
}
|
||||
|
||||
/* Concatenate two lists */
|
||||
val nums1 = mutableListOf(6, 8, 7, 10, 9)
|
||||
nums.addAll(nums1)
|
||||
println("After concatenating list nums1 to nums, get nums = $nums")
|
||||
|
||||
/* Sort list */
|
||||
nums.sort()
|
||||
println("After sorting list, 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
|
||||
|
||||
/* List class */
|
||||
class MyList {
|
||||
private var arr: IntArray = intArrayOf() // Array (stores list elements)
|
||||
private var capacity: Int = 10 // List capacity
|
||||
private var size: Int = 0 // List length (current number of elements)
|
||||
private var extendRatio: Int = 2 // Multiple by which the list capacity is extended each time
|
||||
|
||||
/* Constructor */
|
||||
init {
|
||||
arr = IntArray(capacity)
|
||||
}
|
||||
|
||||
/* Get list length (current number of elements) */
|
||||
fun size(): Int {
|
||||
return size
|
||||
}
|
||||
|
||||
/* Get list capacity */
|
||||
fun capacity(): Int {
|
||||
return capacity
|
||||
}
|
||||
|
||||
/* Update element */
|
||||
fun get(index: Int): Int {
|
||||
// If the index is out of bounds, throw an exception, as below
|
||||
if (index < 0 || index >= size)
|
||||
throw IndexOutOfBoundsException("Index out of bounds")
|
||||
return arr[index]
|
||||
}
|
||||
|
||||
/* Add elements at the end */
|
||||
fun set(index: Int, num: Int) {
|
||||
if (index < 0 || index >= size)
|
||||
throw IndexOutOfBoundsException("Index out of bounds")
|
||||
arr[index] = num
|
||||
}
|
||||
|
||||
/* Direct traversal of list elements */
|
||||
fun add(num: Int) {
|
||||
// When the number of elements exceeds capacity, trigger the extension mechanism
|
||||
if (size == capacity())
|
||||
extendCapacity()
|
||||
arr[size] = num
|
||||
// Update the number of elements
|
||||
size++
|
||||
}
|
||||
|
||||
/* Sort list */
|
||||
fun insert(index: Int, num: Int) {
|
||||
if (index < 0 || index >= size)
|
||||
throw IndexOutOfBoundsException("Index out of bounds")
|
||||
// When the number of elements exceeds capacity, trigger the extension mechanism
|
||||
if (size == capacity())
|
||||
extendCapacity()
|
||||
// Move all elements after index index forward by one position
|
||||
for (j in size - 1 downTo index)
|
||||
arr[j + 1] = arr[j]
|
||||
arr[index] = num
|
||||
// Update the number of elements
|
||||
size++
|
||||
}
|
||||
|
||||
/* Remove element */
|
||||
fun remove(index: Int): Int {
|
||||
if (index < 0 || index >= size)
|
||||
throw IndexOutOfBoundsException("Index out of bounds")
|
||||
val num = arr[index]
|
||||
// Move all elements after index forward by one position
|
||||
for (j in index..<size - 1)
|
||||
arr[j] = arr[j + 1]
|
||||
// Update the number of elements
|
||||
size--
|
||||
// Return the removed element
|
||||
return num
|
||||
}
|
||||
|
||||
/* Driver Code */
|
||||
fun extendCapacity() {
|
||||
// Create a new array with length extendRatio times the original array and copy the original array to the new array
|
||||
arr = arr.copyOf(capacity() * extendRatio)
|
||||
// Add elements at the end
|
||||
capacity = arr.size
|
||||
}
|
||||
|
||||
/* Convert list to array */
|
||||
fun toArray(): IntArray {
|
||||
val size = size()
|
||||
// Elements enqueue
|
||||
val arr = IntArray(size)
|
||||
for (i in 0..<size) {
|
||||
arr[i] = get(i)
|
||||
}
|
||||
return arr
|
||||
}
|
||||
}
|
||||
|
||||
/* Driver Code */
|
||||
fun main() {
|
||||
/* Initialize list */
|
||||
val nums = MyList()
|
||||
/* Direct traversal of list elements */
|
||||
nums.add(1)
|
||||
nums.add(3)
|
||||
nums.add(2)
|
||||
nums.add(5)
|
||||
nums.add(4)
|
||||
println("List nums = ${nums.toArray().contentToString()}, capacity = ${nums.capacity()}, length = ${nums.size()}")
|
||||
|
||||
/* Sort list */
|
||||
nums.insert(3, 6)
|
||||
println("Insert number 6 at index 3, get nums = ${nums.toArray().contentToString()}")
|
||||
|
||||
/* Remove element */
|
||||
nums.remove(3)
|
||||
println("Delete element at index 3, get nums = ${nums.toArray().contentToString()}")
|
||||
|
||||
/* Update element */
|
||||
val num = nums.get(1)
|
||||
println("Access element at index 1, get num = $num")
|
||||
|
||||
/* Add elements at the end */
|
||||
nums.set(1, 0)
|
||||
println("Update element at index 1 to 0, get nums = ${nums.toArray().contentToString()}")
|
||||
|
||||
/* Test capacity expansion mechanism */
|
||||
for (i in 0..9) {
|
||||
// At i = 5, the list length will exceed the list capacity, triggering the expansion mechanism
|
||||
nums.add(i)
|
||||
}
|
||||
println("After expansion, list nums = ${nums.toArray().contentToString()}, capacity = ${nums.capacity()}, length = ${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
|
||||
|
||||
/* Backtracking algorithm: N queens */
|
||||
fun backtrack(
|
||||
row: Int,
|
||||
n: Int,
|
||||
state: MutableList<MutableList<String>>,
|
||||
res: MutableList<MutableList<MutableList<String>>?>,
|
||||
cols: BooleanArray,
|
||||
diags1: BooleanArray,
|
||||
diags2: BooleanArray
|
||||
) {
|
||||
// When all rows are placed, record the solution
|
||||
if (row == n) {
|
||||
val copyState = mutableListOf<MutableList<String>>()
|
||||
for (sRow in state) {
|
||||
copyState.add(sRow.toMutableList())
|
||||
}
|
||||
res.add(copyState)
|
||||
return
|
||||
}
|
||||
// Traverse all columns
|
||||
for (col in 0..<n) {
|
||||
// Calculate the main diagonal and anti-diagonal corresponding to this cell
|
||||
val diag1 = row - col + n - 1
|
||||
val diag2 = row + col
|
||||
// Pruning: do not allow queens to exist in the column, main diagonal, and anti-diagonal of this cell
|
||||
if (!cols[col] && !diags1[diag1] && !diags2[diag2]) {
|
||||
// Attempt: place the queen in this cell
|
||||
state[row][col] = "Q"
|
||||
diags2[diag2] = true
|
||||
diags1[diag1] = diags2[diag2]
|
||||
cols[col] = diags1[diag1]
|
||||
// Place the next row
|
||||
backtrack(row + 1, n, state, res, cols, diags1, diags2)
|
||||
// Backtrack: restore this cell to an empty cell
|
||||
state[row][col] = "#"
|
||||
diags2[diag2] = false
|
||||
diags1[diag1] = diags2[diag2]
|
||||
cols[col] = diags1[diag1]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Solve N queens */
|
||||
fun nQueens(n: Int): MutableList<MutableList<MutableList<String>>?> {
|
||||
// Initialize an n*n chessboard, where 'Q' represents a queen and '#' represents an empty cell
|
||||
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) // Record whether there is a queen in the column
|
||||
val diags1 = BooleanArray(2 * n - 1) // Record whether there is a queen on the main diagonal
|
||||
val diags2 = BooleanArray(2 * n - 1) // Record whether there is a queen on the anti-diagonal
|
||||
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("Input board size is $n")
|
||||
println("Total queen placement solutions: ${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
|
||||
|
||||
/* Backtracking algorithm: Permutations I */
|
||||
fun backtrack(
|
||||
state: MutableList<Int>,
|
||||
choices: IntArray,
|
||||
selected: BooleanArray,
|
||||
res: MutableList<MutableList<Int>?>
|
||||
) {
|
||||
// When the state length equals the number of elements, record the solution
|
||||
if (state.size == choices.size) {
|
||||
res.add(state.toMutableList())
|
||||
return
|
||||
}
|
||||
// Traverse all choices
|
||||
for (i in choices.indices) {
|
||||
val choice = choices[i]
|
||||
// Pruning: do not allow repeated selection of elements
|
||||
if (!selected[i]) {
|
||||
// Attempt: make choice, update state
|
||||
selected[i] = true
|
||||
state.add(choice)
|
||||
// Proceed to the next round of selection
|
||||
backtrack(state, choices, selected, res)
|
||||
// Backtrack: undo choice, restore to previous state
|
||||
selected[i] = false
|
||||
state.removeAt(state.size - 1)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Permutations I */
|
||||
fun permutationsI(nums: IntArray): MutableList<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("Input array nums = ${nums.contentToString()}")
|
||||
println("All permutations 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
|
||||
|
||||
/* Backtracking algorithm: Permutations II */
|
||||
fun backtrack(
|
||||
state: MutableList<Int>,
|
||||
choices: IntArray,
|
||||
selected: BooleanArray,
|
||||
res: MutableList<MutableList<Int>?>
|
||||
) {
|
||||
// When the state length equals the number of elements, record the solution
|
||||
if (state.size == choices.size) {
|
||||
res.add(state.toMutableList())
|
||||
return
|
||||
}
|
||||
// Traverse all choices
|
||||
val duplicated = HashSet<Int>()
|
||||
for (i in choices.indices) {
|
||||
val choice = choices[i]
|
||||
// Pruning: do not allow repeated selection of elements and do not allow repeated selection of equal elements
|
||||
if (!selected[i] && !duplicated.contains(choice)) {
|
||||
// Attempt: make choice, update state
|
||||
duplicated.add(choice) // Record the selected element value
|
||||
selected[i] = true
|
||||
state.add(choice)
|
||||
// Proceed to the next round of selection
|
||||
backtrack(state, choices, selected, res)
|
||||
// Backtrack: undo choice, restore to previous state
|
||||
selected[i] = false
|
||||
state.removeAt(state.size - 1)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Permutations II */
|
||||
fun permutationsII(nums: IntArray): MutableList<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("Input array nums = ${nums.contentToString()}")
|
||||
println("All permutations 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
|
||||
|
||||
/* Preorder traversal: Example 1 */
|
||||
fun preOrder(root: TreeNode?) {
|
||||
if (root == null) {
|
||||
return
|
||||
}
|
||||
if (root._val == 7) {
|
||||
// Record solution
|
||||
res!!.add(root)
|
||||
}
|
||||
preOrder(root.left)
|
||||
preOrder(root.right)
|
||||
}
|
||||
|
||||
/* Driver Code */
|
||||
fun main() {
|
||||
val root = TreeNode.listToTree(mutableListOf(1, 7, 3, 4, 5, 6, 7))
|
||||
println("\nInitialize binary tree")
|
||||
printTree(root)
|
||||
|
||||
// Preorder traversal
|
||||
res = mutableListOf()
|
||||
preOrder(root)
|
||||
|
||||
println("\nOutput all nodes with value 7")
|
||||
val vals = mutableListOf<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
|
||||
|
||||
/* Preorder traversal: Example 2 */
|
||||
fun preOrder(root: TreeNode?) {
|
||||
if (root == null) {
|
||||
return
|
||||
}
|
||||
// Attempt
|
||||
path!!.add(root)
|
||||
if (root._val == 7) {
|
||||
// Record solution
|
||||
res!!.add(path!!.toMutableList())
|
||||
}
|
||||
preOrder(root.left)
|
||||
preOrder(root.right)
|
||||
// Backtrack
|
||||
path!!.removeAt(path!!.size - 1)
|
||||
}
|
||||
|
||||
/* Driver Code */
|
||||
fun main() {
|
||||
val root = TreeNode.listToTree(mutableListOf(1, 7, 3, 4, 5, 6, 7))
|
||||
println("\nInitialize binary tree")
|
||||
printTree(root)
|
||||
|
||||
// Preorder traversal
|
||||
path = mutableListOf()
|
||||
res = mutableListOf()
|
||||
preOrder(root)
|
||||
|
||||
println("\nOutput all paths from root node to node 7")
|
||||
for (path in res!!) {
|
||||
val _vals = mutableListOf<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
|
||||
|
||||
/* Preorder traversal: Example 3 */
|
||||
fun preOrder(root: TreeNode?) {
|
||||
// Pruning
|
||||
if (root == null || root._val == 3) {
|
||||
return
|
||||
}
|
||||
// Attempt
|
||||
path!!.add(root)
|
||||
if (root._val == 7) {
|
||||
// Record solution
|
||||
res!!.add(path!!.toMutableList())
|
||||
}
|
||||
preOrder(root.left)
|
||||
preOrder(root.right)
|
||||
// Backtrack
|
||||
path!!.removeAt(path!!.size - 1)
|
||||
}
|
||||
|
||||
/* Driver Code */
|
||||
fun main() {
|
||||
val root = TreeNode.listToTree(mutableListOf(1, 7, 3, 4, 5, 6, 7))
|
||||
println("\nInitialize binary tree")
|
||||
printTree(root)
|
||||
|
||||
// Preorder traversal
|
||||
path = mutableListOf()
|
||||
res = mutableListOf()
|
||||
preOrder(root)
|
||||
|
||||
println("\nOutput all paths from root node to node 7, paths do not include nodes with value 3")
|
||||
for (path in res!!) {
|
||||
val _vals = mutableListOf<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
|
||||
|
||||
/* Check if the current state is a solution */
|
||||
fun isSolution(state: MutableList<TreeNode?>): Boolean {
|
||||
return state.isNotEmpty() && state[state.size - 1]?._val == 7
|
||||
}
|
||||
|
||||
/* Record solution */
|
||||
fun recordSolution(state: MutableList<TreeNode?>?, res: MutableList<MutableList<TreeNode?>?>) {
|
||||
res.add(state!!.toMutableList())
|
||||
}
|
||||
|
||||
/* Check if the choice is valid under the current state */
|
||||
fun isValid(state: MutableList<TreeNode?>?, choice: TreeNode?): Boolean {
|
||||
return choice != null && choice._val != 3
|
||||
}
|
||||
|
||||
/* Update state */
|
||||
fun makeChoice(state: MutableList<TreeNode?>, choice: TreeNode?) {
|
||||
state.add(choice)
|
||||
}
|
||||
|
||||
/* Restore state */
|
||||
fun undoChoice(state: MutableList<TreeNode?>, choice: TreeNode?) {
|
||||
state.removeLast()
|
||||
}
|
||||
|
||||
/* Backtracking algorithm: Example 3 */
|
||||
fun backtrack(
|
||||
state: MutableList<TreeNode?>,
|
||||
choices: MutableList<TreeNode?>,
|
||||
res: MutableList<MutableList<TreeNode?>?>
|
||||
) {
|
||||
// Check if it is a solution
|
||||
if (isSolution(state)) {
|
||||
// Record solution
|
||||
recordSolution(state, res)
|
||||
}
|
||||
// Traverse all choices
|
||||
for (choice in choices) {
|
||||
// Pruning: check if the choice is valid
|
||||
if (isValid(state, choice)) {
|
||||
// Attempt: make choice, update state
|
||||
makeChoice(state, choice)
|
||||
// Proceed to the next round of selection
|
||||
backtrack(state, mutableListOf(choice!!.left, choice.right), res)
|
||||
// Backtrack: undo choice, restore to previous state
|
||||
undoChoice(state, choice)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Driver Code */
|
||||
fun main() {
|
||||
val root = TreeNode.listToTree(mutableListOf(1, 7, 3, 4, 5, 6, 7))
|
||||
println("\nInitialize binary tree")
|
||||
printTree(root)
|
||||
|
||||
// Backtracking algorithm
|
||||
val res = mutableListOf<MutableList<TreeNode?>?>()
|
||||
backtrack(mutableListOf(), mutableListOf(root), res)
|
||||
|
||||
println("\nOutput all paths from root node to node 7, requiring paths do not include nodes with value 3")
|
||||
for (path in res) {
|
||||
val vals = mutableListOf<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
|
||||
|
||||
/* Backtracking algorithm: Subset sum I */
|
||||
fun backtrack(
|
||||
state: MutableList<Int>,
|
||||
target: Int,
|
||||
choices: IntArray,
|
||||
start: Int,
|
||||
res: MutableList<MutableList<Int>?>
|
||||
) {
|
||||
// When the subset sum equals target, record the solution
|
||||
if (target == 0) {
|
||||
res.add(state.toMutableList())
|
||||
return
|
||||
}
|
||||
// Traverse all choices
|
||||
// Pruning 2: start traversing from start to avoid generating duplicate subsets
|
||||
for (i in start..<choices.size) {
|
||||
// Pruning 1: if the subset sum exceeds target, end the loop directly
|
||||
// This is because the array is sorted, and later elements are larger, so the subset sum will definitely exceed target
|
||||
if (target - choices[i] < 0) {
|
||||
break
|
||||
}
|
||||
// Attempt: make choice, update target, start
|
||||
state.add(choices[i])
|
||||
// Proceed to the next round of selection
|
||||
backtrack(state, target - choices[i], choices, i, res)
|
||||
// Backtrack: undo choice, restore to previous state
|
||||
state.removeAt(state.size - 1)
|
||||
}
|
||||
}
|
||||
|
||||
/* Solve subset sum I */
|
||||
fun subsetSumI(nums: IntArray, target: Int): MutableList<MutableList<Int>?> {
|
||||
val state = mutableListOf<Int>() // State (subset)
|
||||
nums.sort() // Sort nums
|
||||
val start = 0 // Start point for traversal
|
||||
val res = mutableListOf<MutableList<Int>?>() // Result list (subset list)
|
||||
backtrack(state, target, nums, start, res)
|
||||
return res
|
||||
}
|
||||
|
||||
/* Driver Code */
|
||||
fun main() {
|
||||
val nums = intArrayOf(3, 4, 5)
|
||||
val target = 9
|
||||
|
||||
val res = subsetSumI(nums, target)
|
||||
|
||||
println("Input array nums = ${nums.contentToString()}, target = $target")
|
||||
println("All subsets with sum equal to $target res = $res")
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
/**
|
||||
* File: subset_sum_i_native.kt
|
||||
* Created Time: 2024-01-25
|
||||
* Author: curtishd (1023632660@qq.com)
|
||||
*/
|
||||
|
||||
package chapter_backtracking.subset_sum_i_naive
|
||||
|
||||
/* Backtracking algorithm: Subset sum I */
|
||||
fun backtrack(
|
||||
state: MutableList<Int>,
|
||||
target: Int,
|
||||
total: Int,
|
||||
choices: IntArray,
|
||||
res: MutableList<MutableList<Int>?>
|
||||
) {
|
||||
// When the subset sum equals target, record the solution
|
||||
if (total == target) {
|
||||
res.add(state.toMutableList())
|
||||
return
|
||||
}
|
||||
// Traverse all choices
|
||||
for (i in choices.indices) {
|
||||
// Pruning: if the subset sum exceeds target, skip this choice
|
||||
if (total + choices[i] > target) {
|
||||
continue
|
||||
}
|
||||
// Attempt: make choice, update element sum total
|
||||
state.add(choices[i])
|
||||
// Proceed to the next round of selection
|
||||
backtrack(state, target, total + choices[i], choices, res)
|
||||
// Backtrack: undo choice, restore to previous state
|
||||
state.removeAt(state.size - 1)
|
||||
}
|
||||
}
|
||||
|
||||
/* Solve subset sum I (including duplicate subsets) */
|
||||
fun subsetSumINaive(nums: IntArray, target: Int): MutableList<MutableList<Int>?> {
|
||||
val state = mutableListOf<Int>() // State (subset)
|
||||
val total = 0 // Subset sum
|
||||
val res = mutableListOf<MutableList<Int>?>() // Result list (subset list)
|
||||
backtrack(state, target, total, nums, res)
|
||||
return res
|
||||
}
|
||||
|
||||
/* Driver Code */
|
||||
fun main() {
|
||||
val nums = intArrayOf(3, 4, 5)
|
||||
val target = 9
|
||||
val res = subsetSumINaive(nums, target)
|
||||
|
||||
println("Input array nums = ${nums.contentToString()}, target = $target")
|
||||
println("All subsets with sum equal to $target res = $res")
|
||||
println("Please note that this method outputs results containing duplicate sets")
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
/**
|
||||
* File: subset_sum_ii.kt
|
||||
* Created Time: 2024-01-25
|
||||
* Author: curtishd (1023632660@qq.com)
|
||||
*/
|
||||
|
||||
package chapter_backtracking.subset_sum_ii
|
||||
|
||||
/* Backtracking algorithm: Subset sum II */
|
||||
fun backtrack(
|
||||
state: MutableList<Int>,
|
||||
target: Int,
|
||||
choices: IntArray,
|
||||
start: Int,
|
||||
res: MutableList<MutableList<Int>?>
|
||||
) {
|
||||
// When the subset sum equals target, record the solution
|
||||
if (target == 0) {
|
||||
res.add(state.toMutableList())
|
||||
return
|
||||
}
|
||||
// Traverse all choices
|
||||
// Pruning 2: start traversing from start to avoid generating duplicate subsets
|
||||
// Pruning 3: start traversing from start to avoid repeatedly selecting the same element
|
||||
for (i in start..<choices.size) {
|
||||
// Pruning 1: if the subset sum exceeds target, end the loop directly
|
||||
// This is because the array is sorted, and later elements are larger, so the subset sum will definitely exceed target
|
||||
if (target - choices[i] < 0) {
|
||||
break
|
||||
}
|
||||
// Pruning 4: if this element equals the left element, it means this search branch is duplicate, skip it directly
|
||||
if (i > start && choices[i] == choices[i - 1]) {
|
||||
continue
|
||||
}
|
||||
// Attempt: make choice, update target, start
|
||||
state.add(choices[i])
|
||||
// Proceed to the next round of selection
|
||||
backtrack(state, target - choices[i], choices, i + 1, res)
|
||||
// Backtrack: undo choice, restore to previous state
|
||||
state.removeAt(state.size - 1)
|
||||
}
|
||||
}
|
||||
|
||||
/* Solve subset sum II */
|
||||
fun subsetSumII(nums: IntArray, target: Int): MutableList<MutableList<Int>?> {
|
||||
val state = mutableListOf<Int>() // State (subset)
|
||||
nums.sort() // Sort nums
|
||||
val start = 0 // Start point for traversal
|
||||
val res = mutableListOf<MutableList<Int>?>() // Result list (subset list)
|
||||
backtrack(state, target, nums, start, res)
|
||||
return res
|
||||
}
|
||||
|
||||
/* Driver Code */
|
||||
fun main() {
|
||||
val nums = intArrayOf(4, 4, 5)
|
||||
val target = 9
|
||||
val res = subsetSumII(nums, target)
|
||||
|
||||
println("Input array nums = ${nums.contentToString()}, target = $target")
|
||||
println("All subsets with sum equal to $target res = $res")
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
/**
|
||||
* File: iteration.kt
|
||||
* Created Time: 2024-01-25
|
||||
* Author: curtishd (1023632660@qq.com)
|
||||
*/
|
||||
|
||||
package chapter_computational_complexity.iteration
|
||||
|
||||
/* for loop */
|
||||
fun forLoop(n: Int): Int {
|
||||
var res = 0
|
||||
// Sum 1, 2, ..., n-1, n
|
||||
for (i in 1..n) {
|
||||
res += i
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
/* while loop */
|
||||
fun whileLoop(n: Int): Int {
|
||||
var res = 0
|
||||
var i = 1 // Initialize condition variable
|
||||
// Sum 1, 2, ..., n-1, n
|
||||
while (i <= n) {
|
||||
res += i
|
||||
i++ // Update condition variable
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
/* while loop (two updates) */
|
||||
fun whileLoopII(n: Int): Int {
|
||||
var res = 0
|
||||
var i = 1 // Initialize condition variable
|
||||
// Sum 1, 4, 10, ...
|
||||
while (i <= n) {
|
||||
res += i
|
||||
// Update condition variable
|
||||
i++
|
||||
i *= 2
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
/* Nested for loop */
|
||||
fun nestedForLoop(n: Int): String {
|
||||
val res = StringBuilder()
|
||||
// Loop i = 1, 2, ..., n-1, n
|
||||
for (i in 1..n) {
|
||||
// Loop j = 1, 2, ..., n-1, n
|
||||
for (j in 1..n) {
|
||||
res.append(" ($i, $j), ")
|
||||
}
|
||||
}
|
||||
return res.toString()
|
||||
}
|
||||
|
||||
/* Driver Code */
|
||||
fun main() {
|
||||
val n = 5
|
||||
var res: Int
|
||||
|
||||
res = forLoop(n)
|
||||
println("\nFor loop sum result res = $res")
|
||||
|
||||
res = whileLoop(n)
|
||||
println("\nWhile loop sum result res = $res")
|
||||
|
||||
res = whileLoopII(n)
|
||||
println("\nWhile loop (two updates) sum result res = $res")
|
||||
|
||||
val resStr = nestedForLoop(n)
|
||||
println("\nNested for loop traversal result $resStr")
|
||||
}
|
||||
@@ -0,0 +1,78 @@
|
||||
/**
|
||||
* File: recursion.kt
|
||||
* Created Time: 2024-01-25
|
||||
* Author: curtishd (1023632660@qq.com)
|
||||
*/
|
||||
|
||||
package chapter_computational_complexity.recursion
|
||||
|
||||
import java.util.*
|
||||
|
||||
/* Recursion */
|
||||
fun recur(n: Int): Int {
|
||||
// Termination condition
|
||||
if (n == 1)
|
||||
return 1
|
||||
// Descend: recursive call
|
||||
val res = recur(n - 1)
|
||||
// Return: return result
|
||||
return n + res
|
||||
}
|
||||
|
||||
/* Simulate recursion using iteration */
|
||||
fun forLoopRecur(n: Int): Int {
|
||||
// Use an explicit stack to simulate the system call stack
|
||||
val stack = Stack<Int>()
|
||||
var res = 0
|
||||
// Descend: recursive call
|
||||
for (i in n downTo 0) {
|
||||
// Simulate "recurse" with "push"
|
||||
stack.push(i)
|
||||
}
|
||||
// Return: return result
|
||||
while (stack.isNotEmpty()) {
|
||||
// Simulate "return" with "pop"
|
||||
res += stack.pop()
|
||||
}
|
||||
// res = 1+2+3+...+n
|
||||
return res
|
||||
}
|
||||
|
||||
/* Tail recursion */
|
||||
tailrec fun tailRecur(n: Int, res: Int): Int {
|
||||
// Add tailrec keyword to enable tail recursion optimization
|
||||
// Termination condition
|
||||
if (n == 0)
|
||||
return res
|
||||
// Tail recursive call
|
||||
return tailRecur(n - 1, res + n)
|
||||
}
|
||||
|
||||
/* Fibonacci sequence: recursion */
|
||||
fun fib(n: Int): Int {
|
||||
// Termination condition f(1) = 0, f(2) = 1
|
||||
if (n == 1 || n == 2)
|
||||
return n - 1
|
||||
// Recursive call f(n) = f(n-1) + f(n-2)
|
||||
val res = fib(n - 1) + fib(n - 2)
|
||||
// Return result f(n)
|
||||
return res
|
||||
}
|
||||
|
||||
/* Driver Code */
|
||||
fun main() {
|
||||
val n = 5
|
||||
var res: Int
|
||||
|
||||
res = recur(n)
|
||||
println("\nRecursion sum result res = $res")
|
||||
|
||||
res = forLoopRecur(n)
|
||||
println("\nUsing iteration to simulate recursion sum result res = $res")
|
||||
|
||||
res = tailRecur(n, 0)
|
||||
println("\nTail recursion sum result res = $res")
|
||||
|
||||
res = fib(n)
|
||||
println("\nThe ${n}th Fibonacci number is $res")
|
||||
}
|
||||
@@ -0,0 +1,109 @@
|
||||
/**
|
||||
* File: space_complexity.kt
|
||||
* Created Time: 2024-01-25
|
||||
* Author: curtishd (1023632660@qq.com)
|
||||
*/
|
||||
|
||||
package chapter_computational_complexity.space_complexity
|
||||
|
||||
import utils.ListNode
|
||||
import utils.TreeNode
|
||||
import utils.printTree
|
||||
|
||||
/* Function */
|
||||
fun function(): Int {
|
||||
// Perform some operations
|
||||
return 0
|
||||
}
|
||||
|
||||
/* Constant order */
|
||||
fun constant(n: Int) {
|
||||
// Constants, variables, objects occupy O(1) space
|
||||
val a = 0
|
||||
var b = 0
|
||||
val nums = Array(10000) { 0 }
|
||||
val node = ListNode(0)
|
||||
// Variables in the loop occupy O(1) space
|
||||
for (i in 0..<n) {
|
||||
val c = 0
|
||||
}
|
||||
// Functions in the loop occupy O(1) space
|
||||
for (i in 0..<n) {
|
||||
function()
|
||||
}
|
||||
}
|
||||
|
||||
/* Linear order */
|
||||
fun linear(n: Int) {
|
||||
// Array of length n uses O(n) space
|
||||
val nums = Array(n) { 0 }
|
||||
// A list of length n occupies O(n) space
|
||||
val nodes = mutableListOf<ListNode>()
|
||||
for (i in 0..<n) {
|
||||
nodes.add(ListNode(i))
|
||||
}
|
||||
// A hash table of length n occupies O(n) space
|
||||
val map = mutableMapOf<Int, String>()
|
||||
for (i in 0..<n) {
|
||||
map[i] = i.toString()
|
||||
}
|
||||
}
|
||||
|
||||
/* Linear order (recursive implementation) */
|
||||
fun linearRecur(n: Int) {
|
||||
println("Recursion n = $n")
|
||||
if (n == 1)
|
||||
return
|
||||
linearRecur(n - 1)
|
||||
}
|
||||
|
||||
/* Exponential order */
|
||||
fun quadratic(n: Int) {
|
||||
// Matrix uses O(n^2) space
|
||||
val numMatrix = arrayOfNulls<Array<Int>?>(n)
|
||||
// 2D list uses O(n^2) space
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
/* Quadratic order (recursive implementation) */
|
||||
tailrec fun quadraticRecur(n: Int): Int {
|
||||
if (n <= 0)
|
||||
return 0
|
||||
// Array nums has length n, n-1, ..., 2, 1
|
||||
val nums = Array(n) { 0 }
|
||||
println("In recursion n = $n, nums length = ${nums.size}")
|
||||
return quadraticRecur(n - 1)
|
||||
}
|
||||
|
||||
/* Driver Code */
|
||||
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 order
|
||||
constant(n)
|
||||
// Linear order
|
||||
linear(n)
|
||||
linearRecur(n)
|
||||
// Exponential order
|
||||
quadratic(n)
|
||||
quadraticRecur(n)
|
||||
// Exponential order
|
||||
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
|
||||
|
||||
/* Constant order */
|
||||
fun constant(n: Int): Int {
|
||||
var count = 0
|
||||
val size = 100000
|
||||
for (i in 0..<size)
|
||||
count++
|
||||
return count
|
||||
}
|
||||
|
||||
/* Linear order */
|
||||
fun linear(n: Int): Int {
|
||||
var count = 0
|
||||
for (i in 0..<n)
|
||||
count++
|
||||
return count
|
||||
}
|
||||
|
||||
/* Linear order (traversing array) */
|
||||
fun arrayTraversal(nums: IntArray): Int {
|
||||
var count = 0
|
||||
// Number of iterations is proportional to the array length
|
||||
for (num in nums) {
|
||||
count++
|
||||
}
|
||||
return count
|
||||
}
|
||||
|
||||
/* Exponential order */
|
||||
fun quadratic(n: Int): Int {
|
||||
var count = 0
|
||||
// Number of iterations is quadratically related to the data size n
|
||||
for (i in 0..<n) {
|
||||
for (j in 0..<n) {
|
||||
count++
|
||||
}
|
||||
}
|
||||
return count
|
||||
}
|
||||
|
||||
/* Quadratic order (bubble sort) */
|
||||
fun bubbleSort(nums: IntArray): Int {
|
||||
var count = 0 // Counter
|
||||
// Outer loop: unsorted range is [0, i]
|
||||
for (i in nums.size - 1 downTo 1) {
|
||||
// Inner loop: swap the largest element in the unsorted range [0, i] to the rightmost end of that range
|
||||
for (j in 0..<i) {
|
||||
if (nums[j] > nums[j + 1]) {
|
||||
// Swap nums[j] and nums[j + 1]
|
||||
val temp = nums[j]
|
||||
nums[j] = nums[j + 1]
|
||||
nums[j + 1] = temp
|
||||
count += 3 // Element swap includes 3 unit operations
|
||||
}
|
||||
}
|
||||
}
|
||||
return count
|
||||
}
|
||||
|
||||
/* Exponential order (loop implementation) */
|
||||
fun exponential(n: Int): Int {
|
||||
var count = 0
|
||||
var base = 1
|
||||
// Cells divide into two every round, forming sequence 1, 2, 4, 8, ..., 2^(n-1)
|
||||
for (i in 0..<n) {
|
||||
for (j in 0..<base) {
|
||||
count++
|
||||
}
|
||||
base *= 2
|
||||
}
|
||||
// count = 1 + 2 + 4 + 8 + .. + 2^(n-1) = 2^n - 1
|
||||
return count
|
||||
}
|
||||
|
||||
/* Exponential order (recursive implementation) */
|
||||
fun expRecur(n: Int): Int {
|
||||
if (n == 1) {
|
||||
return 1
|
||||
}
|
||||
return expRecur(n - 1) + expRecur(n - 1) + 1
|
||||
}
|
||||
|
||||
/* Logarithmic order (loop implementation) */
|
||||
fun logarithmic(n: Int): Int {
|
||||
var n1 = n
|
||||
var count = 0
|
||||
while (n1 > 1) {
|
||||
n1 /= 2
|
||||
count++
|
||||
}
|
||||
return count
|
||||
}
|
||||
|
||||
/* Logarithmic order (recursive implementation) */
|
||||
fun logRecur(n: Int): Int {
|
||||
if (n <= 1)
|
||||
return 0
|
||||
return logRecur(n / 2) + 1
|
||||
}
|
||||
|
||||
/* Linearithmic order */
|
||||
fun linearLogRecur(n: Int): Int {
|
||||
if (n <= 1)
|
||||
return 1
|
||||
var count = linearLogRecur(n / 2) + linearLogRecur(n / 2)
|
||||
for (i in 0..<n) {
|
||||
count++
|
||||
}
|
||||
return count
|
||||
}
|
||||
|
||||
/* Factorial order (recursive implementation) */
|
||||
fun factorialRecur(n: Int): Int {
|
||||
if (n == 0)
|
||||
return 1
|
||||
var count = 0
|
||||
// Split from 1 into n
|
||||
for (i in 0..<n) {
|
||||
count += factorialRecur(n - 1)
|
||||
}
|
||||
return count
|
||||
}
|
||||
|
||||
/* Driver Code */
|
||||
fun main() {
|
||||
// You can modify n to run and observe the trend of the number of operations for various complexities
|
||||
val n = 8
|
||||
println("Input data size n = $n")
|
||||
|
||||
var count = constant(n)
|
||||
println("Constant-time operations count = $count")
|
||||
|
||||
count = linear(n)
|
||||
println("Linear-time operations count = $count")
|
||||
count = arrayTraversal(IntArray(n))
|
||||
println("Linear-time (array traversal) operations count = $count")
|
||||
|
||||
count = quadratic(n)
|
||||
println("Quadratic-time operations count = $count")
|
||||
val nums = IntArray(n)
|
||||
for (i in 0..<n)
|
||||
nums[i] = n - i // [n,n-1,...,2,1]
|
||||
count = bubbleSort(nums)
|
||||
println("Quadratic-time (bubble sort) operations count = $count")
|
||||
|
||||
count = exponential(n)
|
||||
println("Exponential-time (iterative) operations count = $count")
|
||||
count = expRecur(n)
|
||||
println("Exponential-time (recursive) operations count = $count")
|
||||
|
||||
count = logarithmic(n)
|
||||
println("Logarithmic-time (iterative) operations count = $count")
|
||||
count = logRecur(n)
|
||||
println("Logarithmic-time (recursive) operations count = $count")
|
||||
|
||||
count = linearLogRecur(n)
|
||||
println("Linearithmic-time (recursive) operations count = $count")
|
||||
|
||||
count = factorialRecur(n)
|
||||
println("Factorial-time (recursive) operations count = $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
|
||||
|
||||
/* Generate an array with elements { 1, 2, ..., n }, order shuffled */
|
||||
fun randomNumbers(n: Int): Array<Int?> {
|
||||
val nums = IntArray(n)
|
||||
// Generate array nums = { 1, 2, 3, ..., n }
|
||||
for (i in 0..<n) {
|
||||
nums[i] = i + 1
|
||||
}
|
||||
// Randomly shuffle array elements
|
||||
nums.shuffle()
|
||||
val res = arrayOfNulls<Int>(n)
|
||||
for (i in 0..<n) {
|
||||
res[i] = nums[i]
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
/* Find the index of number 1 in array nums */
|
||||
fun findOne(nums: Array<Int?>): Int {
|
||||
for (i in nums.indices) {
|
||||
// When element 1 is at the head of the array, best time complexity O(1) is achieved
|
||||
// When element 1 is at the tail of the array, worst time complexity O(n) is achieved
|
||||
if (nums[i] == 1)
|
||||
return i
|
||||
}
|
||||
return -1
|
||||
}
|
||||
|
||||
/* Driver Code */
|
||||
fun main() {
|
||||
for (i in 0..9) {
|
||||
val n = 100
|
||||
val nums = randomNumbers(n)
|
||||
val index = findOne(nums)
|
||||
println("\nArray [ 1, 2, ..., n ] after shuffling = ${nums.contentToString()}")
|
||||
println("Index of number 1 is $index")
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
/**
|
||||
* File: binary_search_recur.kt
|
||||
* Created Time: 2024-01-25
|
||||
* Author: curtishd (1023632660@qq.com)
|
||||
*/
|
||||
|
||||
package chapter_divide_and_conquer.binary_search_recur
|
||||
|
||||
/* Binary search: problem f(i, j) */
|
||||
fun dfs(
|
||||
nums: IntArray,
|
||||
target: Int,
|
||||
i: Int,
|
||||
j: Int
|
||||
): Int {
|
||||
// If the interval is empty, it means there is no target element, return -1
|
||||
if (i > j) {
|
||||
return -1
|
||||
}
|
||||
// Calculate the midpoint index m
|
||||
val m = (i + j) / 2
|
||||
return if (nums[m] < target) {
|
||||
// Recursion subproblem f(m+1, j)
|
||||
dfs(nums, target, m + 1, j)
|
||||
} else if (nums[m] > target) {
|
||||
// Recursion subproblem f(i, m-1)
|
||||
dfs(nums, target, i, m - 1)
|
||||
} else {
|
||||
// Found the target element, return its index
|
||||
m
|
||||
}
|
||||
}
|
||||
|
||||
/* Binary search */
|
||||
fun binarySearch(nums: IntArray, target: Int): Int {
|
||||
val n = nums.size
|
||||
// Solve the problem f(0, n-1)
|
||||
return dfs(nums, target, 0, n - 1)
|
||||
}
|
||||
|
||||
/* Driver Code */
|
||||
fun main() {
|
||||
val target = 6
|
||||
val nums = intArrayOf(1, 3, 6, 8, 12, 15, 23, 26, 31, 35)
|
||||
|
||||
// Binary search (closed interval on both sides)
|
||||
val index = binarySearch(nums, target)
|
||||
println("Index of target element 6 = $index")
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
/**
|
||||
* File: build_tree.kt
|
||||
* Created Time: 2024-01-25
|
||||
* Author: curtishd (1023632660@qq.com)
|
||||
*/
|
||||
|
||||
package chapter_divide_and_conquer.build_tree
|
||||
|
||||
import utils.TreeNode
|
||||
import utils.printTree
|
||||
|
||||
/* Build binary tree: divide and conquer */
|
||||
fun dfs(
|
||||
preorder: IntArray,
|
||||
inorderMap: Map<Int?, Int?>,
|
||||
i: Int,
|
||||
l: Int,
|
||||
r: Int
|
||||
): TreeNode? {
|
||||
// Terminate when the subtree interval is empty
|
||||
if (r - l < 0) return null
|
||||
// Initialize the root node
|
||||
val root = TreeNode(preorder[i])
|
||||
// Query m to divide the left and right subtrees
|
||||
val m = inorderMap[preorder[i]]!!
|
||||
// Subproblem: build the left subtree
|
||||
root.left = dfs(preorder, inorderMap, i + 1, l, m - 1)
|
||||
// Subproblem: build the right subtree
|
||||
root.right = dfs(preorder, inorderMap, i + 1 + m - l, m + 1, r)
|
||||
// Return the root node
|
||||
return root
|
||||
}
|
||||
|
||||
/* Build binary tree */
|
||||
fun buildTree(preorder: IntArray, inorder: IntArray): TreeNode? {
|
||||
// Initialize hash map, storing the mapping from inorder elements to indices
|
||||
val inorderMap = HashMap<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("Pre-order traversal = ${preorder.contentToString()}")
|
||||
println("In-order traversal = ${inorder.contentToString()}")
|
||||
|
||||
val root = buildTree(preorder, inorder)
|
||||
println("The constructed binary tree is:")
|
||||
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
|
||||
|
||||
/* Move a disk */
|
||||
fun move(src: MutableList<Int>, tar: MutableList<Int>) {
|
||||
// Take out a disk from the top of src
|
||||
val pan = src.removeAt(src.size - 1)
|
||||
// Place the disk on top of tar
|
||||
tar.add(pan)
|
||||
}
|
||||
|
||||
/* Solve the Tower of Hanoi problem f(i) */
|
||||
fun dfs(i: Int, src: MutableList<Int>, buf: MutableList<Int>, tar: MutableList<Int>) {
|
||||
// If there is only one disk left in src, move it directly to tar
|
||||
if (i == 1) {
|
||||
move(src, tar)
|
||||
return
|
||||
}
|
||||
// Subproblem f(i-1): move the top i-1 disks from src to buf using tar
|
||||
dfs(i - 1, src, tar, buf)
|
||||
// Subproblem f(1): move the remaining disk from src to tar
|
||||
move(src, tar)
|
||||
// Subproblem f(i-1): move the top i-1 disks from buf to tar using src
|
||||
dfs(i - 1, buf, src, tar)
|
||||
}
|
||||
|
||||
/* Solve the Tower of Hanoi problem */
|
||||
fun solveHanota(A: MutableList<Int>, B: MutableList<Int>, C: MutableList<Int>) {
|
||||
val n = A.size
|
||||
// Move the top n disks from A to C using B
|
||||
dfs(n, A, B, C)
|
||||
}
|
||||
|
||||
/* Driver Code */
|
||||
fun main() {
|
||||
// The tail of the list is the top of the rod
|
||||
val A = mutableListOf(5, 4, 3, 2, 1)
|
||||
val B = mutableListOf<Int>()
|
||||
val C = mutableListOf<Int>()
|
||||
println("In initial state:")
|
||||
println("A = $A")
|
||||
println("B = $B")
|
||||
println("C = $C")
|
||||
|
||||
solveHanota(A, B, C)
|
||||
|
||||
println("After disk movement is complete:")
|
||||
println("A = $A")
|
||||
println("B = $B")
|
||||
println("C = $C")
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
/**
|
||||
* File: climbing_stairs_backtrack.kt
|
||||
* Created Time: 2024-01-25
|
||||
* Author: curtishd (1023632660@qq.com)
|
||||
*/
|
||||
|
||||
package chapter_dynamic_programming
|
||||
|
||||
/* Backtracking */
|
||||
fun backtrack(
|
||||
choices: MutableList<Int>,
|
||||
state: Int,
|
||||
n: Int,
|
||||
res: MutableList<Int>
|
||||
) {
|
||||
// When climbing to the n-th stair, add 1 to the solution count
|
||||
if (state == n)
|
||||
res[0] = res[0] + 1
|
||||
// Traverse all choices
|
||||
for (choice in choices) {
|
||||
// Pruning: not allowed to go beyond the n-th stair
|
||||
if (state + choice > n) continue
|
||||
// Attempt: make choice, update state
|
||||
backtrack(choices, state + choice, n, res)
|
||||
// Backtrack
|
||||
}
|
||||
}
|
||||
|
||||
/* Climbing stairs: Backtracking */
|
||||
fun climbingStairsBacktrack(n: Int): Int {
|
||||
val choices = mutableListOf(1, 2) // Can choose to climb up 1 or 2 stairs
|
||||
val state = 0 // Start climbing from the 0-th stair
|
||||
val res = mutableListOf<Int>()
|
||||
res.add(0) // Use res[0] to record the solution count
|
||||
backtrack(choices, state, n, res)
|
||||
return res[0]
|
||||
}
|
||||
|
||||
/* Driver Code */
|
||||
fun main() {
|
||||
val n = 9
|
||||
|
||||
val res = climbingStairsBacktrack(n)
|
||||
println("Climbing $n stairs has $res solutions")
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
/**
|
||||
* File: climbing_stairs_constraint_dp.kt
|
||||
* Created Time: 2024-01-25
|
||||
* Author: curtishd (1023632660@qq.com)
|
||||
*/
|
||||
|
||||
package chapter_dynamic_programming
|
||||
|
||||
/* Climbing stairs with constraint: Dynamic programming */
|
||||
fun climbingStairsConstraintDP(n: Int): Int {
|
||||
if (n == 1 || n == 2) {
|
||||
return 1
|
||||
}
|
||||
// Initialize dp table, used to store solutions to subproblems
|
||||
val dp = Array(n + 1) { IntArray(3) }
|
||||
// Initial state: preset the solution to the smallest subproblem
|
||||
dp[1][1] = 1
|
||||
dp[1][2] = 0
|
||||
dp[2][1] = 0
|
||||
dp[2][2] = 1
|
||||
// State transition: gradually solve larger subproblems from smaller ones
|
||||
for (i in 3..n) {
|
||||
dp[i][1] = dp[i - 1][2]
|
||||
dp[i][2] = dp[i - 2][1] + dp[i - 2][2]
|
||||
}
|
||||
return dp[n][1] + dp[n][2]
|
||||
}
|
||||
|
||||
/* Driver Code */
|
||||
fun main() {
|
||||
val n = 9
|
||||
|
||||
val res = climbingStairsConstraintDP(n)
|
||||
println("Climbing $n stairs has $res solutions")
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
/**
|
||||
* File: climbing_stairs_dfs.kt
|
||||
* Created Time: 2024-01-25
|
||||
* Author: curtishd (1023632660@qq.com)
|
||||
*/
|
||||
|
||||
package chapter_dynamic_programming
|
||||
|
||||
/* Search */
|
||||
fun dfs(i: Int): Int {
|
||||
// Known dp[1] and dp[2], return them
|
||||
if (i == 1 || i == 2) return i
|
||||
// dp[i] = dp[i-1] + dp[i-2]
|
||||
val count = dfs(i - 1) + dfs(i - 2)
|
||||
return count
|
||||
}
|
||||
|
||||
/* Climbing stairs: Search */
|
||||
fun climbingStairsDFS(n: Int): Int {
|
||||
return dfs(n)
|
||||
}
|
||||
|
||||
/* Driver Code */
|
||||
fun main() {
|
||||
val n = 9
|
||||
|
||||
val res = climbingStairsDFS(n)
|
||||
println("Climbing $n stairs has $res solutions")
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
/**
|
||||
* File: climbing_stairs_dfs_mem.kt
|
||||
* Created Time: 2024-01-25
|
||||
* Author: curtishd (1023632660@qq.com)
|
||||
*/
|
||||
|
||||
package chapter_dynamic_programming
|
||||
|
||||
/* Memoization search */
|
||||
fun dfs(i: Int, mem: IntArray): Int {
|
||||
// Known dp[1] and dp[2], return them
|
||||
if (i == 1 || i == 2) return i
|
||||
// If record dp[i] exists, return it directly
|
||||
if (mem[i] != -1) return mem[i]
|
||||
// dp[i] = dp[i-1] + dp[i-2]
|
||||
val count = dfs(i - 1, mem) + dfs(i - 2, mem)
|
||||
// Record dp[i]
|
||||
mem[i] = count
|
||||
return count
|
||||
}
|
||||
|
||||
/* Climbing stairs: Memoization search */
|
||||
fun climbingStairsDFSMem(n: Int): Int {
|
||||
// mem[i] records the total number of solutions to climb to the i-th stair, -1 means no record
|
||||
val mem = IntArray(n + 1)
|
||||
mem.fill(-1)
|
||||
return dfs(n, mem)
|
||||
}
|
||||
|
||||
/* Driver Code */
|
||||
fun main() {
|
||||
val n = 9
|
||||
|
||||
val res = climbingStairsDFSMem(n)
|
||||
println("Climbing $n stairs has $res solutions")
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
/**
|
||||
* File: climbing_stairs_dp.kt
|
||||
* Created Time: 2024-01-25
|
||||
* Author: curtishd (1023632660@qq.com)
|
||||
*/
|
||||
|
||||
package chapter_dynamic_programming
|
||||
|
||||
/* Climbing stairs: Dynamic programming */
|
||||
fun climbingStairsDP(n: Int): Int {
|
||||
if (n == 1 || n == 2) return n
|
||||
// Initialize dp table, used to store solutions to subproblems
|
||||
val dp = IntArray(n + 1)
|
||||
// Initial state: preset the solution to the smallest subproblem
|
||||
dp[1] = 1
|
||||
dp[2] = 2
|
||||
// State transition: gradually solve larger subproblems from smaller ones
|
||||
for (i in 3..n) {
|
||||
dp[i] = dp[i - 1] + dp[i - 2]
|
||||
}
|
||||
return dp[n]
|
||||
}
|
||||
|
||||
/* Climbing stairs: Space-optimized dynamic programming */
|
||||
fun climbingStairsDPComp(n: Int): Int {
|
||||
if (n == 1 || n == 2) return n
|
||||
var a = 1
|
||||
var b = 2
|
||||
for (i in 3..n) {
|
||||
val temp = b
|
||||
b += a
|
||||
a = temp
|
||||
}
|
||||
return b
|
||||
}
|
||||
|
||||
/* Driver Code */
|
||||
fun main() {
|
||||
val n = 9
|
||||
|
||||
var res = climbingStairsDP(n)
|
||||
println("Climbing $n stairs has $res solutions")
|
||||
|
||||
res = climbingStairsDPComp(n)
|
||||
println("Climbing $n stairs has $res solutions")
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
/**
|
||||
* File: coin_change.kt
|
||||
* Created Time: 2024-01-25
|
||||
* Author: curtishd (1023632660@qq.com)
|
||||
*/
|
||||
|
||||
package chapter_dynamic_programming
|
||||
|
||||
import kotlin.math.min
|
||||
|
||||
/* Coin change: Dynamic programming */
|
||||
fun coinChangeDP(coins: IntArray, amt: Int): Int {
|
||||
val n = coins.size
|
||||
val MAX = amt + 1
|
||||
// Initialize dp table
|
||||
val dp = Array(n + 1) { IntArray(amt + 1) }
|
||||
// State transition: first row and first column
|
||||
for (a in 1..amt) {
|
||||
dp[0][a] = MAX
|
||||
}
|
||||
// State transition: rest of the rows and columns
|
||||
for (i in 1..n) {
|
||||
for (a in 1..amt) {
|
||||
if (coins[i - 1] > a) {
|
||||
// If exceeds target amount, don't select coin i
|
||||
dp[i][a] = dp[i - 1][a]
|
||||
} else {
|
||||
// The smaller value between not selecting and selecting coin i
|
||||
dp[i][a] = min(dp[i - 1][a], dp[i][a - coins[i - 1]] + 1)
|
||||
}
|
||||
}
|
||||
}
|
||||
return if (dp[n][amt] != MAX) dp[n][amt] else -1
|
||||
}
|
||||
|
||||
/* Coin change: Space-optimized dynamic programming */
|
||||
fun coinChangeDPComp(coins: IntArray, amt: Int): Int {
|
||||
val n = coins.size
|
||||
val MAX = amt + 1
|
||||
// Initialize dp table
|
||||
val dp = IntArray(amt + 1)
|
||||
dp.fill(MAX)
|
||||
dp[0] = 0
|
||||
// State transition
|
||||
for (i in 1..n) {
|
||||
for (a in 1..amt) {
|
||||
if (coins[i - 1] > a) {
|
||||
// If exceeds target amount, don't select coin i
|
||||
dp[a] = dp[a]
|
||||
} else {
|
||||
// The smaller value between not selecting and selecting coin i
|
||||
dp[a] = min(dp[a], dp[a - coins[i - 1]] + 1)
|
||||
}
|
||||
}
|
||||
}
|
||||
return if (dp[amt] != MAX) dp[amt] else -1
|
||||
}
|
||||
|
||||
/* Driver Code */
|
||||
fun main() {
|
||||
val coins = intArrayOf(1, 2, 5)
|
||||
val amt = 4
|
||||
|
||||
// Dynamic programming
|
||||
var res = coinChangeDP(coins, amt)
|
||||
println("Minimum coins needed to make target amount is $res")
|
||||
|
||||
// Space-optimized dynamic programming
|
||||
res = coinChangeDPComp(coins, amt)
|
||||
println("Minimum coins needed to make target amount is $res")
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
/**
|
||||
* File: coin_change_ii.kt
|
||||
* Created Time: 2024-01-25
|
||||
* Author: curtishd (1023632660@qq.com)
|
||||
*/
|
||||
|
||||
package chapter_dynamic_programming
|
||||
|
||||
/* Coin change II: Dynamic programming */
|
||||
fun coinChangeIIDP(coins: IntArray, amt: Int): Int {
|
||||
val n = coins.size
|
||||
// Initialize dp table
|
||||
val dp = Array(n + 1) { IntArray(amt + 1) }
|
||||
// Initialize first column
|
||||
for (i in 0..n) {
|
||||
dp[i][0] = 1
|
||||
}
|
||||
// State transition
|
||||
for (i in 1..n) {
|
||||
for (a in 1..amt) {
|
||||
if (coins[i - 1] > a) {
|
||||
// If exceeds target amount, don't select coin i
|
||||
dp[i][a] = dp[i - 1][a]
|
||||
} else {
|
||||
// Sum of the two options: not selecting and selecting coin i
|
||||
dp[i][a] = dp[i - 1][a] + dp[i][a - coins[i - 1]]
|
||||
}
|
||||
}
|
||||
}
|
||||
return dp[n][amt]
|
||||
}
|
||||
|
||||
/* Coin change II: Space-optimized dynamic programming */
|
||||
fun coinChangeIIDPComp(coins: IntArray, amt: Int): Int {
|
||||
val n = coins.size
|
||||
// Initialize dp table
|
||||
val dp = IntArray(amt + 1)
|
||||
dp[0] = 1
|
||||
// State transition
|
||||
for (i in 1..n) {
|
||||
for (a in 1..amt) {
|
||||
if (coins[i - 1] > a) {
|
||||
// If exceeds target amount, don't select coin i
|
||||
dp[a] = dp[a]
|
||||
} else {
|
||||
// Sum of the two options: not selecting and selecting coin i
|
||||
dp[a] = dp[a] + dp[a - coins[i - 1]]
|
||||
}
|
||||
}
|
||||
}
|
||||
return dp[amt]
|
||||
}
|
||||
|
||||
/* Driver Code */
|
||||
fun main() {
|
||||
val coins = intArrayOf(1, 2, 5)
|
||||
val amt = 5
|
||||
|
||||
// Dynamic programming
|
||||
var res = coinChangeIIDP(coins, amt)
|
||||
println("Number of coin combinations to make target amount is $res")
|
||||
|
||||
// Space-optimized dynamic programming
|
||||
res = coinChangeIIDPComp(coins, amt)
|
||||
println("Number of coin combinations to make target amount is $res")
|
||||
}
|
||||
@@ -0,0 +1,143 @@
|
||||
/**
|
||||
* File: edit_distance.kt
|
||||
* Created Time: 2024-01-25
|
||||
* Author: curtishd (1023632660@qq.com)
|
||||
*/
|
||||
|
||||
package chapter_dynamic_programming
|
||||
|
||||
import kotlin.math.min
|
||||
|
||||
/* Edit distance: Brute-force search */
|
||||
fun editDistanceDFS(
|
||||
s: String,
|
||||
t: String,
|
||||
i: Int,
|
||||
j: Int
|
||||
): Int {
|
||||
// If both s and t are empty, return 0
|
||||
if (i == 0 && j == 0) return 0
|
||||
// If s is empty, return length of t
|
||||
if (i == 0) return j
|
||||
// If t is empty, return length of s
|
||||
if (j == 0) return i
|
||||
// If two characters are equal, skip both characters
|
||||
if (s[i - 1] == t[j - 1]) return editDistanceDFS(s, t, i - 1, j - 1)
|
||||
// Minimum edit steps = minimum edit steps of insert, delete, replace + 1
|
||||
val insert = editDistanceDFS(s, t, i, j - 1)
|
||||
val delete = editDistanceDFS(s, t, i - 1, j)
|
||||
val replace = editDistanceDFS(s, t, i - 1, j - 1)
|
||||
// Return minimum edit steps
|
||||
return min(min(insert, delete), replace) + 1
|
||||
}
|
||||
|
||||
/* Edit distance: Memoization search */
|
||||
fun editDistanceDFSMem(
|
||||
s: String,
|
||||
t: String,
|
||||
mem: Array<IntArray>,
|
||||
i: Int,
|
||||
j: Int
|
||||
): Int {
|
||||
// If both s and t are empty, return 0
|
||||
if (i == 0 && j == 0) return 0
|
||||
// If s is empty, return length of t
|
||||
if (i == 0) return j
|
||||
// If t is empty, return length of s
|
||||
if (j == 0) return i
|
||||
// If there's a record, return it directly
|
||||
if (mem[i][j] != -1) return mem[i][j]
|
||||
// If two characters are equal, skip both characters
|
||||
if (s[i - 1] == t[j - 1]) return editDistanceDFSMem(s, t, mem, i - 1, j - 1)
|
||||
// Minimum edit steps = minimum edit steps of insert, delete, replace + 1
|
||||
val insert = editDistanceDFSMem(s, t, mem, i, j - 1)
|
||||
val delete = editDistanceDFSMem(s, t, mem, i - 1, j)
|
||||
val replace = editDistanceDFSMem(s, t, mem, i - 1, j - 1)
|
||||
// Record and return minimum edit steps
|
||||
mem[i][j] = min(min(insert, delete), replace) + 1
|
||||
return mem[i][j]
|
||||
}
|
||||
|
||||
/* Edit distance: Dynamic programming */
|
||||
fun editDistanceDP(s: String, t: String): Int {
|
||||
val n = s.length
|
||||
val m = t.length
|
||||
val dp = Array(n + 1) { IntArray(m + 1) }
|
||||
// State transition: first row and first column
|
||||
for (i in 1..n) {
|
||||
dp[i][0] = i
|
||||
}
|
||||
for (j in 1..m) {
|
||||
dp[0][j] = j
|
||||
}
|
||||
// State transition: rest of the rows and columns
|
||||
for (i in 1..n) {
|
||||
for (j in 1..m) {
|
||||
if (s[i - 1] == t[j - 1]) {
|
||||
// If two characters are equal, skip both characters
|
||||
dp[i][j] = dp[i - 1][j - 1]
|
||||
} else {
|
||||
// Minimum edit steps = minimum edit steps of insert, delete, replace + 1
|
||||
dp[i][j] = min(min(dp[i][j - 1], dp[i - 1][j]), dp[i - 1][j - 1]) + 1
|
||||
}
|
||||
}
|
||||
}
|
||||
return dp[n][m]
|
||||
}
|
||||
|
||||
/* Edit distance: Space-optimized dynamic programming */
|
||||
fun editDistanceDPComp(s: String, t: String): Int {
|
||||
val n = s.length
|
||||
val m = t.length
|
||||
val dp = IntArray(m + 1)
|
||||
// State transition: first row
|
||||
for (j in 1..m) {
|
||||
dp[j] = j
|
||||
}
|
||||
// State transition: rest of the rows
|
||||
for (i in 1..n) {
|
||||
// State transition: first column
|
||||
var leftup = dp[0] // Temporarily store dp[i-1, j-1]
|
||||
dp[0] = i
|
||||
// State transition: rest of the columns
|
||||
for (j in 1..m) {
|
||||
val temp = dp[j]
|
||||
if (s[i - 1] == t[j - 1]) {
|
||||
// If two characters are equal, skip both characters
|
||||
dp[j] = leftup
|
||||
} else {
|
||||
// Minimum edit steps = minimum edit steps of insert, delete, replace + 1
|
||||
dp[j] = min(min(dp[j - 1], dp[j]), leftup) + 1
|
||||
}
|
||||
leftup = temp // Update for next round's dp[i-1, j-1]
|
||||
}
|
||||
}
|
||||
return dp[m]
|
||||
}
|
||||
|
||||
/* Driver Code */
|
||||
fun main() {
|
||||
val s = "bag"
|
||||
val t = "pack"
|
||||
val n = s.length
|
||||
val m = t.length
|
||||
|
||||
// Brute-force search
|
||||
var res = editDistanceDFS(s, t, n, m)
|
||||
println("Changing $s to $t requires minimum $res edits")
|
||||
|
||||
// Memoization search
|
||||
val mem = Array(n + 1) { IntArray(m + 1) }
|
||||
for (row in mem)
|
||||
row.fill(-1)
|
||||
res = editDistanceDFSMem(s, t, mem, n, m)
|
||||
println("Changing $s to $t requires minimum $res edits")
|
||||
|
||||
// Dynamic programming
|
||||
res = editDistanceDP(s, t)
|
||||
println("Changing $s to $t requires minimum $res edits")
|
||||
|
||||
// Space-optimized dynamic programming
|
||||
res = editDistanceDPComp(s, t)
|
||||
println("Changing $s to $t requires minimum $res edits")
|
||||
}
|
||||
@@ -0,0 +1,125 @@
|
||||
/**
|
||||
* File: knapsack.kt
|
||||
* Created Time: 2024-01-25
|
||||
* Author: curtishd (1023632660@qq.com)
|
||||
*/
|
||||
|
||||
package chapter_dynamic_programming
|
||||
|
||||
import kotlin.math.max
|
||||
|
||||
/* 0-1 knapsack: Brute-force search */
|
||||
fun knapsackDFS(
|
||||
wgt: IntArray,
|
||||
_val: IntArray,
|
||||
i: Int,
|
||||
c: Int
|
||||
): Int {
|
||||
// If all items have been selected or knapsack has no remaining capacity, return value 0
|
||||
if (i == 0 || c == 0) {
|
||||
return 0
|
||||
}
|
||||
// If exceeds knapsack capacity, can only choose not to put it in
|
||||
if (wgt[i - 1] > c) {
|
||||
return knapsackDFS(wgt, _val, i - 1, c)
|
||||
}
|
||||
// Calculate the maximum value of not putting in and putting in item i
|
||||
val no = knapsackDFS(wgt, _val, i - 1, c)
|
||||
val yes = knapsackDFS(wgt, _val, i - 1, c - wgt[i - 1]) + _val[i - 1]
|
||||
// Return the larger value of the two options
|
||||
return max(no, yes)
|
||||
}
|
||||
|
||||
/* 0-1 knapsack: Memoization search */
|
||||
fun knapsackDFSMem(
|
||||
wgt: IntArray,
|
||||
_val: IntArray,
|
||||
mem: Array<IntArray>,
|
||||
i: Int,
|
||||
c: Int
|
||||
): Int {
|
||||
// If all items have been selected or knapsack has no remaining capacity, return value 0
|
||||
if (i == 0 || c == 0) {
|
||||
return 0
|
||||
}
|
||||
// If there's a record, return it directly
|
||||
if (mem[i][c] != -1) {
|
||||
return mem[i][c]
|
||||
}
|
||||
// If exceeds knapsack capacity, can only choose not to put it in
|
||||
if (wgt[i - 1] > c) {
|
||||
return knapsackDFSMem(wgt, _val, mem, i - 1, c)
|
||||
}
|
||||
// Calculate the maximum value of not putting in and putting in item i
|
||||
val no = knapsackDFSMem(wgt, _val, mem, i - 1, c)
|
||||
val yes = knapsackDFSMem(wgt, _val, mem, i - 1, c - wgt[i - 1]) + _val[i - 1]
|
||||
// Record and return the larger value of the two options
|
||||
mem[i][c] = max(no, yes)
|
||||
return mem[i][c]
|
||||
}
|
||||
|
||||
/* 0-1 knapsack: Dynamic programming */
|
||||
fun knapsackDP(wgt: IntArray, _val: IntArray, cap: Int): Int {
|
||||
val n = wgt.size
|
||||
// Initialize dp table
|
||||
val dp = Array(n + 1) { IntArray(cap + 1) }
|
||||
// State transition
|
||||
for (i in 1..n) {
|
||||
for (c in 1..cap) {
|
||||
if (wgt[i - 1] > c) {
|
||||
// If exceeds knapsack capacity, don't select item i
|
||||
dp[i][c] = dp[i - 1][c]
|
||||
} else {
|
||||
// The larger value between not selecting and selecting item i
|
||||
dp[i][c] = max(dp[i - 1][c], dp[i - 1][c - wgt[i - 1]] + _val[i - 1])
|
||||
}
|
||||
}
|
||||
}
|
||||
return dp[n][cap]
|
||||
}
|
||||
|
||||
/* 0-1 knapsack: Space-optimized dynamic programming */
|
||||
fun knapsackDPComp(wgt: IntArray, _val: IntArray, cap: Int): Int {
|
||||
val n = wgt.size
|
||||
// Initialize dp table
|
||||
val dp = IntArray(cap + 1)
|
||||
// State transition
|
||||
for (i in 1..n) {
|
||||
// Traverse in reverse order
|
||||
for (c in cap downTo 1) {
|
||||
if (wgt[i - 1] <= c) {
|
||||
// The larger value between not selecting and selecting item i
|
||||
dp[c] = max(dp[c], dp[c - wgt[i - 1]] + _val[i - 1])
|
||||
}
|
||||
}
|
||||
}
|
||||
return dp[cap]
|
||||
}
|
||||
|
||||
/* Driver Code */
|
||||
fun main() {
|
||||
val wgt = intArrayOf(10, 20, 30, 40, 50)
|
||||
val _val = intArrayOf(50, 120, 150, 210, 240)
|
||||
val cap = 50
|
||||
val n = wgt.size
|
||||
|
||||
// Brute-force search
|
||||
var res = knapsackDFS(wgt, _val, n, cap)
|
||||
println("Maximum item value not exceeding knapsack capacity is $res")
|
||||
|
||||
// Memoization search
|
||||
val mem = Array(n + 1) { IntArray(cap + 1) }
|
||||
for (row in mem) {
|
||||
row.fill(-1)
|
||||
}
|
||||
res = knapsackDFSMem(wgt, _val, mem, n, cap)
|
||||
println("Maximum item value not exceeding knapsack capacity is $res")
|
||||
|
||||
// Dynamic programming
|
||||
res = knapsackDP(wgt, _val, cap)
|
||||
println("Maximum item value not exceeding knapsack capacity is $res")
|
||||
|
||||
// Space-optimized dynamic programming
|
||||
res = knapsackDPComp(wgt, _val, cap)
|
||||
println("Maximum item value not exceeding knapsack capacity is $res")
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
/**
|
||||
* File: min_cost_climbing_stairs_dp.kt
|
||||
* Created Time: 2024-01-25
|
||||
* Author: curtishd (1023632660@qq.com)
|
||||
*/
|
||||
|
||||
package chapter_dynamic_programming
|
||||
|
||||
import kotlin.math.min
|
||||
|
||||
/* Minimum cost climbing stairs: Dynamic programming */
|
||||
fun minCostClimbingStairsDP(cost: IntArray): Int {
|
||||
val n = cost.size - 1
|
||||
if (n == 1 || n == 2) return cost[n]
|
||||
// Initialize dp table, used to store solutions to subproblems
|
||||
val dp = IntArray(n + 1)
|
||||
// Initial state: preset the solution to the smallest subproblem
|
||||
dp[1] = cost[1]
|
||||
dp[2] = cost[2]
|
||||
// State transition: gradually solve larger subproblems from smaller ones
|
||||
for (i in 3..n) {
|
||||
dp[i] = min(dp[i - 1], dp[i - 2]) + cost[i]
|
||||
}
|
||||
return dp[n]
|
||||
}
|
||||
|
||||
/* Minimum cost climbing stairs: Space-optimized dynamic programming */
|
||||
fun minCostClimbingStairsDPComp(cost: IntArray): Int {
|
||||
val n = cost.size - 1
|
||||
if (n == 1 || n == 2) return cost[n]
|
||||
var a = cost[1]
|
||||
var b = cost[2]
|
||||
for (i in 3..n) {
|
||||
val tmp = b
|
||||
b = min(a, tmp) + cost[i]
|
||||
a = tmp
|
||||
}
|
||||
return b
|
||||
}
|
||||
|
||||
/* Driver Code */
|
||||
fun main() {
|
||||
val cost = intArrayOf(0, 1, 10, 1, 1, 1, 10, 1, 1, 10, 1)
|
||||
println("Input stair cost list is ${cost.contentToString()}")
|
||||
|
||||
var res = minCostClimbingStairsDP(cost)
|
||||
println("Minimum cost to climb stairs is $res")
|
||||
|
||||
res = minCostClimbingStairsDPComp(cost)
|
||||
println("Minimum cost to climb stairs is $res")
|
||||
}
|
||||
@@ -0,0 +1,132 @@
|
||||
/**
|
||||
* File: min_path_sum.kt
|
||||
* Created Time: 2024-01-25
|
||||
* Author: curtishd (1023632660@qq.com)
|
||||
*/
|
||||
|
||||
package chapter_dynamic_programming
|
||||
|
||||
import kotlin.math.min
|
||||
|
||||
/* Minimum path sum: Brute-force search */
|
||||
fun minPathSumDFS(grid: Array<IntArray>, i: Int, j: Int): Int {
|
||||
// If it's the top-left cell, terminate the search
|
||||
if (i == 0 && j == 0) {
|
||||
return grid[0][0]
|
||||
}
|
||||
// If row or column index is out of bounds, return +∞ cost
|
||||
if (i < 0 || j < 0) {
|
||||
return Int.MAX_VALUE
|
||||
}
|
||||
// Calculate the minimum path cost from top-left to (i-1, j) and (i, j-1)
|
||||
val up = minPathSumDFS(grid, i - 1, j)
|
||||
val left = minPathSumDFS(grid, i, j - 1)
|
||||
// Return the minimum path cost from top-left to (i, j)
|
||||
return min(left, up) + grid[i][j]
|
||||
}
|
||||
|
||||
/* Minimum path sum: Memoization search */
|
||||
fun minPathSumDFSMem(
|
||||
grid: Array<IntArray>,
|
||||
mem: Array<IntArray>,
|
||||
i: Int,
|
||||
j: Int
|
||||
): Int {
|
||||
// If it's the top-left cell, terminate the search
|
||||
if (i == 0 && j == 0) {
|
||||
return grid[0][0]
|
||||
}
|
||||
// If row or column index is out of bounds, return +∞ cost
|
||||
if (i < 0 || j < 0) {
|
||||
return Int.MAX_VALUE
|
||||
}
|
||||
// If there's a record, return it directly
|
||||
if (mem[i][j] != -1) {
|
||||
return mem[i][j]
|
||||
}
|
||||
// Minimum path cost for left and upper cells
|
||||
val up = minPathSumDFSMem(grid, mem, i - 1, j)
|
||||
val left = minPathSumDFSMem(grid, mem, i, j - 1)
|
||||
// Record and return the minimum path cost from top-left to (i, j)
|
||||
mem[i][j] = min(left, up) + grid[i][j]
|
||||
return mem[i][j]
|
||||
}
|
||||
|
||||
/* Minimum path sum: Dynamic programming */
|
||||
fun minPathSumDP(grid: Array<IntArray>): Int {
|
||||
val n = grid.size
|
||||
val m = grid[0].size
|
||||
// Initialize dp table
|
||||
val dp = Array(n) { IntArray(m) }
|
||||
dp[0][0] = grid[0][0]
|
||||
// State transition: first row
|
||||
for (j in 1..<m) {
|
||||
dp[0][j] = dp[0][j - 1] + grid[0][j]
|
||||
}
|
||||
// State transition: first column
|
||||
for (i in 1..<n) {
|
||||
dp[i][0] = dp[i - 1][0] + grid[i][0]
|
||||
}
|
||||
// State transition: rest of the rows and columns
|
||||
for (i in 1..<n) {
|
||||
for (j in 1..<m) {
|
||||
dp[i][j] = min(dp[i][j - 1], dp[i - 1][j]) + grid[i][j]
|
||||
}
|
||||
}
|
||||
return dp[n - 1][m - 1]
|
||||
}
|
||||
|
||||
/* Minimum path sum: Space-optimized dynamic programming */
|
||||
fun minPathSumDPComp(grid: Array<IntArray>): Int {
|
||||
val n = grid.size
|
||||
val m = grid[0].size
|
||||
// Initialize dp table
|
||||
val dp = IntArray(m)
|
||||
// State transition: first row
|
||||
dp[0] = grid[0][0]
|
||||
for (j in 1..<m) {
|
||||
dp[j] = dp[j - 1] + grid[0][j]
|
||||
}
|
||||
// State transition: rest of the rows
|
||||
for (i in 1..<n) {
|
||||
// State transition: first column
|
||||
dp[0] = dp[0] + grid[i][0]
|
||||
// State transition: rest of the columns
|
||||
for (j in 1..<m) {
|
||||
dp[j] = min(dp[j - 1], dp[j]) + grid[i][j]
|
||||
}
|
||||
}
|
||||
return dp[m - 1]
|
||||
}
|
||||
|
||||
/* 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
|
||||
|
||||
// Brute-force search
|
||||
var res = minPathSumDFS(grid, n - 1, m - 1)
|
||||
println("Minimum path sum from top-left to bottom-right is $res")
|
||||
|
||||
// Memoization search
|
||||
val mem = Array(n) { IntArray(m) }
|
||||
for (row in mem) {
|
||||
row.fill(-1)
|
||||
}
|
||||
res = minPathSumDFSMem(grid, mem, n - 1, m - 1)
|
||||
println("Minimum path sum from top-left to bottom-right is $res")
|
||||
|
||||
// Dynamic programming
|
||||
res = minPathSumDP(grid)
|
||||
println("Minimum path sum from top-left to bottom-right is $res")
|
||||
|
||||
// Space-optimized dynamic programming
|
||||
res = minPathSumDPComp(grid)
|
||||
println("Minimum path sum from top-left to bottom-right is $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
|
||||
|
||||
/* Unbounded knapsack: Dynamic programming */
|
||||
fun unboundedKnapsackDP(wgt: IntArray, _val: IntArray, cap: Int): Int {
|
||||
val n = wgt.size
|
||||
// Initialize dp table
|
||||
val dp = Array(n + 1) { IntArray(cap + 1) }
|
||||
// State transition
|
||||
for (i in 1..n) {
|
||||
for (c in 1..cap) {
|
||||
if (wgt[i - 1] > c) {
|
||||
// If exceeds knapsack capacity, don't select item i
|
||||
dp[i][c] = dp[i - 1][c]
|
||||
} else {
|
||||
// The larger value between not selecting and selecting item i
|
||||
dp[i][c] = max(dp[i - 1][c], dp[i][c - wgt[i - 1]] + _val[i - 1])
|
||||
}
|
||||
}
|
||||
}
|
||||
return dp[n][cap]
|
||||
}
|
||||
|
||||
/* Unbounded knapsack: Space-optimized dynamic programming */
|
||||
fun unboundedKnapsackDPComp(
|
||||
wgt: IntArray,
|
||||
_val: IntArray,
|
||||
cap: Int
|
||||
): Int {
|
||||
val n = wgt.size
|
||||
// Initialize dp table
|
||||
val dp = IntArray(cap + 1)
|
||||
// State transition
|
||||
for (i in 1..n) {
|
||||
for (c in 1..cap) {
|
||||
if (wgt[i - 1] > c) {
|
||||
// If exceeds knapsack capacity, don't select item i
|
||||
dp[c] = dp[c]
|
||||
} else {
|
||||
// The larger value between not selecting and selecting item i
|
||||
dp[c] = max(dp[c], dp[c - wgt[i - 1]] + _val[i - 1])
|
||||
}
|
||||
}
|
||||
}
|
||||
return dp[cap]
|
||||
}
|
||||
|
||||
/* Driver Code */
|
||||
fun main() {
|
||||
val wgt = intArrayOf(1, 2, 3)
|
||||
val _val = intArrayOf(5, 11, 15)
|
||||
val cap = 4
|
||||
|
||||
// Dynamic programming
|
||||
var res = unboundedKnapsackDP(wgt, _val, cap)
|
||||
println("Maximum item value not exceeding knapsack capacity is $res")
|
||||
|
||||
// Space-optimized dynamic programming
|
||||
res = unboundedKnapsackDPComp(wgt, _val, cap)
|
||||
println("Maximum item value not exceeding knapsack capacity is $res")
|
||||
}
|
||||
@@ -0,0 +1,121 @@
|
||||
/**
|
||||
* File: graph_adjacency_list.kt
|
||||
* Created Time: 2024-01-25
|
||||
* Author: curtishd (1023632660@qq.com)
|
||||
*/
|
||||
|
||||
package chapter_graph
|
||||
|
||||
import utils.Vertex
|
||||
|
||||
/* Undirected graph class based on adjacency list */
|
||||
class GraphAdjList(edges: Array<Array<Vertex?>>) {
|
||||
// Adjacency list, key: vertex, value: all adjacent vertices of that vertex
|
||||
val adjList = HashMap<Vertex, MutableList<Vertex>>()
|
||||
|
||||
/* Constructor */
|
||||
init {
|
||||
// Add all vertices and edges
|
||||
for (edge in edges) {
|
||||
addVertex(edge[0]!!)
|
||||
addVertex(edge[1]!!)
|
||||
addEdge(edge[0]!!, edge[1]!!)
|
||||
}
|
||||
}
|
||||
|
||||
/* Get the number of vertices */
|
||||
fun size(): Int {
|
||||
return adjList.size
|
||||
}
|
||||
|
||||
/* Add edge */
|
||||
fun addEdge(vet1: Vertex, vet2: Vertex) {
|
||||
if (!adjList.containsKey(vet1) || !adjList.containsKey(vet2) || vet1 == vet2)
|
||||
throw IllegalArgumentException()
|
||||
// Add edge vet1 - vet2
|
||||
adjList[vet1]?.add(vet2)
|
||||
adjList[vet2]?.add(vet1)
|
||||
}
|
||||
|
||||
/* Remove edge */
|
||||
fun removeEdge(vet1: Vertex, vet2: Vertex) {
|
||||
if (!adjList.containsKey(vet1) || !adjList.containsKey(vet2) || vet1 == vet2)
|
||||
throw IllegalArgumentException()
|
||||
// Remove edge vet1 - vet2
|
||||
adjList[vet1]?.remove(vet2)
|
||||
adjList[vet2]?.remove(vet1)
|
||||
}
|
||||
|
||||
/* Add vertex */
|
||||
fun addVertex(vet: Vertex) {
|
||||
if (adjList.containsKey(vet))
|
||||
return
|
||||
// Add a new linked list in the adjacency list
|
||||
adjList[vet] = mutableListOf()
|
||||
}
|
||||
|
||||
/* Remove vertex */
|
||||
fun removeVertex(vet: Vertex) {
|
||||
if (!adjList.containsKey(vet))
|
||||
throw IllegalArgumentException()
|
||||
// Remove the linked list corresponding to vertex vet in the adjacency list
|
||||
adjList.remove(vet)
|
||||
// Traverse the linked lists of other vertices and remove all edges containing vet
|
||||
for (list in adjList.values) {
|
||||
list.remove(vet)
|
||||
}
|
||||
}
|
||||
|
||||
/* Print adjacency list */
|
||||
fun print() {
|
||||
println("Adjacency list =")
|
||||
for (pair in adjList.entries) {
|
||||
val tmp = mutableListOf<Int>()
|
||||
for (vertex in pair.value) {
|
||||
tmp.add(vertex._val)
|
||||
}
|
||||
println("${pair.key._val}: $tmp,")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Driver Code */
|
||||
fun main() {
|
||||
/* Add edge */
|
||||
val v = Vertex.valsToVets(intArrayOf(1, 3, 2, 5, 4))
|
||||
val edges = arrayOf(
|
||||
arrayOf(v[0], v[1]),
|
||||
arrayOf(v[0], v[3]),
|
||||
arrayOf(v[1], v[2]),
|
||||
arrayOf(v[2], v[3]),
|
||||
arrayOf(v[2], v[4]),
|
||||
arrayOf(v[3], v[4])
|
||||
)
|
||||
val graph = GraphAdjList(edges)
|
||||
println("\nAfter initialization, graph is")
|
||||
graph.print()
|
||||
|
||||
/* Add edge */
|
||||
// Vertices 1, 3 are v[0], v[1]
|
||||
graph.addEdge(v[0]!!, v[2]!!)
|
||||
println("\nAfter adding edge 1-2, graph is")
|
||||
graph.print()
|
||||
|
||||
/* Remove edge */
|
||||
// Vertex 3 is v[1]
|
||||
graph.removeEdge(v[0]!!, v[1]!!)
|
||||
println("\nAfter removing edge 1-3, graph is")
|
||||
graph.print()
|
||||
|
||||
/* Add vertex */
|
||||
val v5 = Vertex(6)
|
||||
graph.addVertex(v5)
|
||||
println("\nAfter adding vertex 6, graph is")
|
||||
graph.print()
|
||||
|
||||
/* Remove vertex */
|
||||
// Vertex 3 is v[1]
|
||||
graph.removeVertex(v[1]!!)
|
||||
println("\nAfter removing vertex 3, graph is")
|
||||
graph.print()
|
||||
}
|
||||
@@ -0,0 +1,134 @@
|
||||
/**
|
||||
* File: graph_adjacency_matrix.kt
|
||||
* Created Time: 2024-01-25
|
||||
* Author: curtishd (1023632660@qq.com)
|
||||
*/
|
||||
|
||||
package chapter_graph
|
||||
|
||||
import utils.printMatrix
|
||||
|
||||
/* Undirected graph class based on adjacency matrix */
|
||||
class GraphAdjMat(vertices: IntArray, edges: Array<IntArray>) {
|
||||
val vertices = mutableListOf<Int>() // Vertex list, where the element represents the "vertex value" and the index represents the "vertex index"
|
||||
val adjMat = mutableListOf<MutableList<Int>>() // Adjacency matrix, where the row and column indices correspond to the "vertex index"
|
||||
|
||||
/* Constructor */
|
||||
init {
|
||||
// Add vertex
|
||||
for (vertex in vertices) {
|
||||
addVertex(vertex)
|
||||
}
|
||||
// Add edge
|
||||
// Note that the edges elements represent vertex indices, i.e., corresponding to the vertices element indices
|
||||
for (edge in edges) {
|
||||
addEdge(edge[0], edge[1])
|
||||
}
|
||||
}
|
||||
|
||||
/* Get the number of vertices */
|
||||
fun size(): Int {
|
||||
return vertices.size
|
||||
}
|
||||
|
||||
/* Add vertex */
|
||||
fun addVertex(_val: Int) {
|
||||
val n = size()
|
||||
// Add the value of the new vertex to the vertex list
|
||||
vertices.add(_val)
|
||||
// Add a row to the adjacency matrix
|
||||
val newRow = mutableListOf<Int>()
|
||||
for (j in 0..<n) {
|
||||
newRow.add(0)
|
||||
}
|
||||
adjMat.add(newRow)
|
||||
// Add a column to the adjacency matrix
|
||||
for (row in adjMat) {
|
||||
row.add(0)
|
||||
}
|
||||
}
|
||||
|
||||
/* Remove vertex */
|
||||
fun removeVertex(index: Int) {
|
||||
if (index >= size())
|
||||
throw IndexOutOfBoundsException()
|
||||
// Remove the vertex at index from the vertex list
|
||||
vertices.removeAt(index)
|
||||
// Remove the row at index from the adjacency matrix
|
||||
adjMat.removeAt(index)
|
||||
// Remove the column at index from the adjacency matrix
|
||||
for (row in adjMat) {
|
||||
row.removeAt(index)
|
||||
}
|
||||
}
|
||||
|
||||
/* Add edge */
|
||||
// Parameters i, j correspond to the vertices element indices
|
||||
fun addEdge(i: Int, j: Int) {
|
||||
// Handle index out of bounds and equality
|
||||
if (i < 0 || j < 0 || i >= size() || j >= size() || i == j)
|
||||
throw IndexOutOfBoundsException()
|
||||
// In an undirected graph, the adjacency matrix is symmetric about the main diagonal, i.e., (i, j) == (j, i)
|
||||
adjMat[i][j] = 1
|
||||
adjMat[j][i] = 1
|
||||
}
|
||||
|
||||
/* Remove edge */
|
||||
// Parameters i, j correspond to the vertices element indices
|
||||
fun removeEdge(i: Int, j: Int) {
|
||||
// Handle index out of bounds and equality
|
||||
if (i < 0 || j < 0 || i >= size() || j >= size() || i == j)
|
||||
throw IndexOutOfBoundsException()
|
||||
adjMat[i][j] = 0
|
||||
adjMat[j][i] = 0
|
||||
}
|
||||
|
||||
/* Print adjacency matrix */
|
||||
fun print() {
|
||||
print("Vertex list = ")
|
||||
println(vertices)
|
||||
println("Adjacency matrix =")
|
||||
printMatrix(adjMat)
|
||||
}
|
||||
}
|
||||
|
||||
/* Driver Code */
|
||||
fun main() {
|
||||
/* Add edge */
|
||||
// Note that the edges elements represent vertex indices, i.e., corresponding to the vertices element indices
|
||||
val vertices = intArrayOf(1, 3, 2, 5, 4)
|
||||
val edges = arrayOf(
|
||||
intArrayOf(0, 1),
|
||||
intArrayOf(0, 3),
|
||||
intArrayOf(1, 2),
|
||||
intArrayOf(2, 3),
|
||||
intArrayOf(2, 4),
|
||||
intArrayOf(3, 4)
|
||||
)
|
||||
val graph = GraphAdjMat(vertices, edges)
|
||||
println("\nAfter initialization, graph is")
|
||||
graph.print()
|
||||
|
||||
/* Add edge */
|
||||
// Add vertex
|
||||
graph.addEdge(0, 2)
|
||||
println("\nAfter adding edge 1-2, graph is")
|
||||
graph.print()
|
||||
|
||||
/* Remove edge */
|
||||
// Vertices 1, 3 have indices 0, 1 respectively
|
||||
graph.removeEdge(0, 1)
|
||||
println("\nAfter removing edge 1-3, graph is")
|
||||
graph.print()
|
||||
|
||||
/* Add vertex */
|
||||
graph.addVertex(6)
|
||||
println("\nAfter adding vertex 6, graph is")
|
||||
graph.print()
|
||||
|
||||
/* Remove vertex */
|
||||
// Vertex 3 has index 1
|
||||
graph.removeVertex(1)
|
||||
println("\nAfter removing vertex 3, graph is")
|
||||
graph.print()
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
/**
|
||||
* File: graph_bfs.kt
|
||||
* Created Time: 2024-01-25
|
||||
* Author: curtishd (1023632660@qq.com)
|
||||
*/
|
||||
|
||||
package chapter_graph
|
||||
|
||||
import utils.Vertex
|
||||
import java.util.*
|
||||
|
||||
/* Breadth-first traversal */
|
||||
// Use adjacency list to represent the graph, in order to obtain all adjacent vertices of a specified vertex
|
||||
fun graphBFS(graph: GraphAdjList, startVet: Vertex): MutableList<Vertex?> {
|
||||
// Vertex traversal sequence
|
||||
val res = mutableListOf<Vertex?>()
|
||||
// Hash set for recording vertices that have been visited
|
||||
val visited = HashSet<Vertex>()
|
||||
visited.add(startVet)
|
||||
// Queue used to implement BFS
|
||||
val que = LinkedList<Vertex>()
|
||||
que.offer(startVet)
|
||||
// Starting from vertex vet, loop until all vertices are visited
|
||||
while (!que.isEmpty()) {
|
||||
val vet = que.poll() // Dequeue the front vertex
|
||||
res.add(vet) // Record visited vertex
|
||||
// Traverse all adjacent vertices of this vertex
|
||||
for (adjVet in graph.adjList[vet]!!) {
|
||||
if (visited.contains(adjVet))
|
||||
continue // Skip vertices that have been visited
|
||||
que.offer(adjVet) // Only enqueue unvisited vertices
|
||||
visited.add(adjVet) // Mark this vertex as visited
|
||||
}
|
||||
}
|
||||
// Return vertex traversal sequence
|
||||
return res
|
||||
}
|
||||
|
||||
/* Driver Code */
|
||||
fun main() {
|
||||
/* Add edge */
|
||||
val v = Vertex.valsToVets(intArrayOf(0, 1, 2, 3, 4, 5, 6, 7, 8, 9))
|
||||
val edges = arrayOf(
|
||||
arrayOf(v[0], v[1]),
|
||||
arrayOf(v[0], v[3]),
|
||||
arrayOf(v[1], v[2]),
|
||||
arrayOf(v[1], v[4]),
|
||||
arrayOf(v[2], v[5]),
|
||||
arrayOf(v[3], v[4]),
|
||||
arrayOf(v[3], v[6]),
|
||||
arrayOf(v[4], v[5]),
|
||||
arrayOf(v[4], v[7]),
|
||||
arrayOf(v[5], v[8]),
|
||||
arrayOf(v[6], v[7]),
|
||||
arrayOf(v[7], v[8])
|
||||
)
|
||||
val graph = GraphAdjList(edges)
|
||||
println("\nAfter initialization, graph is")
|
||||
graph.print()
|
||||
|
||||
/* Breadth-first traversal */
|
||||
val res = graphBFS(graph, v[0]!!)
|
||||
println("\nBreadth-first traversal (BFS) vertex sequence is")
|
||||
println(Vertex.vetsToVals(res))
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
/**
|
||||
* File: graph_dfs.kt
|
||||
* Created Time: 2024-01-25
|
||||
* Author: curtishd (1023632660@qq.com)
|
||||
*/
|
||||
|
||||
package chapter_graph
|
||||
|
||||
import utils.Vertex
|
||||
|
||||
/* Depth-first traversal helper function */
|
||||
fun dfs(
|
||||
graph: GraphAdjList,
|
||||
visited: MutableSet<Vertex?>,
|
||||
res: MutableList<Vertex?>,
|
||||
vet: Vertex?
|
||||
) {
|
||||
res.add(vet) // Record visited vertex
|
||||
visited.add(vet) // Mark this vertex as visited
|
||||
// Traverse all adjacent vertices of this vertex
|
||||
for (adjVet in graph.adjList[vet]!!) {
|
||||
if (visited.contains(adjVet))
|
||||
continue // Skip vertices that have been visited
|
||||
// Recursively visit adjacent vertices
|
||||
dfs(graph, visited, res, adjVet)
|
||||
}
|
||||
}
|
||||
|
||||
/* Depth-first traversal */
|
||||
// Use adjacency list to represent the graph, in order to obtain all adjacent vertices of a specified vertex
|
||||
fun graphDFS(graph: GraphAdjList, startVet: Vertex?): MutableList<Vertex?> {
|
||||
// Vertex traversal sequence
|
||||
val res = mutableListOf<Vertex?>()
|
||||
// Hash set for recording vertices that have been visited
|
||||
val visited = HashSet<Vertex?>()
|
||||
dfs(graph, visited, res, startVet)
|
||||
return res
|
||||
}
|
||||
|
||||
/* Driver Code */
|
||||
fun main() {
|
||||
/* Add edge */
|
||||
val v = Vertex.valsToVets(intArrayOf(0, 1, 2, 3, 4, 5, 6))
|
||||
val edges = arrayOf(
|
||||
arrayOf(v[0], v[1]),
|
||||
arrayOf(v[0], v[3]),
|
||||
arrayOf(v[1], v[2]),
|
||||
arrayOf(v[2], v[5]),
|
||||
arrayOf(v[4], v[5]),
|
||||
arrayOf(v[5], v[6])
|
||||
)
|
||||
val graph = GraphAdjList(edges)
|
||||
println("\nAfter initialization, graph is")
|
||||
graph.print()
|
||||
|
||||
/* Depth-first traversal */
|
||||
val res = graphDFS(graph, v[0])
|
||||
println("\nDepth-first traversal (DFS) vertex sequence is")
|
||||
println(Vertex.vetsToVals(res))
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
/**
|
||||
* File: coin_change_greedy.kt
|
||||
* Created Time: 2024-01-25
|
||||
* Author: curtishd (1023632660@qq.com)
|
||||
*/
|
||||
|
||||
package chapter_greedy
|
||||
|
||||
/* Coin change: Greedy algorithm */
|
||||
fun coinChangeGreedy(coins: IntArray, amt: Int): Int {
|
||||
// Assume coins list is sorted
|
||||
var am = amt
|
||||
var i = coins.size - 1
|
||||
var count = 0
|
||||
// Loop to make greedy choices until no remaining amount
|
||||
while (am > 0) {
|
||||
// Find the coin that is less than and closest to the remaining amount
|
||||
while (i > 0 && coins[i] > am) {
|
||||
i--
|
||||
}
|
||||
// Choose coins[i]
|
||||
am -= coins[i]
|
||||
count++
|
||||
}
|
||||
// If no feasible solution is found, return -1
|
||||
return if (am == 0) count else -1
|
||||
}
|
||||
|
||||
/* Driver Code */
|
||||
fun main() {
|
||||
// Greedy algorithm: Can guarantee finding the global optimal solution
|
||||
var coins = intArrayOf(1, 5, 10, 20, 50, 100)
|
||||
var amt = 186
|
||||
var res = coinChangeGreedy(coins, amt)
|
||||
println("\ncoins = ${coins.contentToString()}, amt = $amt")
|
||||
println("Minimum coins needed to make $amt is $res")
|
||||
|
||||
// Greedy algorithm: Cannot guarantee finding the global optimal solution
|
||||
coins = intArrayOf(1, 20, 50)
|
||||
amt = 60
|
||||
res = coinChangeGreedy(coins, amt)
|
||||
println("\ncoins = ${coins.contentToString()}, amt = $amt")
|
||||
println("Minimum coins needed to make $amt is $res")
|
||||
println("Actually the minimum number needed is 3, i.e., 20 + 20 + 20")
|
||||
|
||||
// Greedy algorithm: Cannot guarantee finding the global optimal solution
|
||||
coins = intArrayOf(1, 49, 50)
|
||||
amt = 98
|
||||
res = coinChangeGreedy(coins, amt)
|
||||
println("\ncoins = ${coins.contentToString()}, amt = $amt")
|
||||
println("Minimum coins needed to make $amt is $res")
|
||||
println("Actually the minimum number needed is 2, i.e., 49 + 49")
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
/**
|
||||
* File: fractional_knapsack.kt
|
||||
* Created Time: 2024-01-25
|
||||
* Author: curtishd (1023632660@qq.com)
|
||||
*/
|
||||
|
||||
package chapter_greedy
|
||||
|
||||
/* Item */
|
||||
class Item(
|
||||
val w: Int, // Item
|
||||
val v: Int // Item value
|
||||
)
|
||||
|
||||
/* Fractional knapsack: Greedy algorithm */
|
||||
fun fractionalKnapsack(wgt: IntArray, _val: IntArray, c: Int): Double {
|
||||
// Create item list with two attributes: weight, value
|
||||
var cap = c
|
||||
val items = arrayOfNulls<Item>(wgt.size)
|
||||
for (i in wgt.indices) {
|
||||
items[i] = Item(wgt[i], _val[i])
|
||||
}
|
||||
// Sort by unit value item.v / item.w from high to low
|
||||
items.sortBy { item: Item? -> -(item!!.v.toDouble() / item.w) }
|
||||
// Loop for greedy selection
|
||||
var res = 0.0
|
||||
for (item in items) {
|
||||
if (item!!.w <= cap) {
|
||||
// If remaining capacity is sufficient, put the entire current item into the knapsack
|
||||
res += item.v
|
||||
cap -= item.w
|
||||
} else {
|
||||
// If remaining capacity is insufficient, put part of the current item into the knapsack
|
||||
res += item.v.toDouble() / item.w * cap
|
||||
// No remaining capacity, so break out of the loop
|
||||
break
|
||||
}
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
/* Driver Code */
|
||||
fun main() {
|
||||
val wgt = intArrayOf(10, 20, 30, 40, 50)
|
||||
val _val = intArrayOf(50, 120, 150, 210, 240)
|
||||
val cap = 50
|
||||
|
||||
// Greedy algorithm
|
||||
val res = fractionalKnapsack(wgt, _val, cap)
|
||||
println("Maximum item value not exceeding knapsack capacity is $res")
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
/**
|
||||
* File: max_capacity.kt
|
||||
* Created Time: 2024-01-25
|
||||
* Author: curtishd (1023632660@qq.com)
|
||||
*/
|
||||
|
||||
package chapter_greedy
|
||||
|
||||
import kotlin.math.max
|
||||
import kotlin.math.min
|
||||
|
||||
/* Max capacity: Greedy algorithm */
|
||||
fun maxCapacity(ht: IntArray): Int {
|
||||
// Initialize i, j to be at both ends of the array
|
||||
var i = 0
|
||||
var j = ht.size - 1
|
||||
// Initial max capacity is 0
|
||||
var res = 0
|
||||
// Loop for greedy selection until the two boards meet
|
||||
while (i < j) {
|
||||
// Update max capacity
|
||||
val cap = min(ht[i], ht[j]) * (j - i)
|
||||
res = max(res, cap)
|
||||
// Move the shorter board inward
|
||||
if (ht[i] < ht[j]) {
|
||||
i++
|
||||
} else {
|
||||
j--
|
||||
}
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
/* Driver Code */
|
||||
fun main() {
|
||||
val ht = intArrayOf(3, 8, 5, 2, 7, 7, 3, 4)
|
||||
|
||||
// Greedy algorithm
|
||||
val res = maxCapacity(ht)
|
||||
println("Maximum capacity is $res")
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
/**
|
||||
* File: max_product_cutting.kt
|
||||
* Created Time: 2024-01-25
|
||||
* Author: curtishd (1023632660@qq.com)
|
||||
*/
|
||||
|
||||
package chapter_greedy
|
||||
|
||||
import kotlin.math.pow
|
||||
|
||||
/* Max product cutting: Greedy algorithm */
|
||||
fun maxProductCutting(n: Int): Int {
|
||||
// When n <= 3, must cut out a 1
|
||||
if (n <= 3) {
|
||||
return 1 * (n - 1)
|
||||
}
|
||||
// Greedily cut out 3, a is the number of 3s, b is the remainder
|
||||
val a = n / 3
|
||||
val b = n % 3
|
||||
if (b == 1) {
|
||||
// When the remainder is 1, convert a pair of 1 * 3 to 2 * 2
|
||||
return 3.0.pow((a - 1)).toInt() * 2 * 2
|
||||
}
|
||||
if (b == 2) {
|
||||
// When the remainder is 2, do nothing
|
||||
return 3.0.pow(a).toInt() * 2 * 2
|
||||
}
|
||||
// When the remainder is 0, do nothing
|
||||
return 3.0.pow(a).toInt()
|
||||
}
|
||||
|
||||
/* Driver Code */
|
||||
fun main() {
|
||||
val n = 58
|
||||
|
||||
// Greedy algorithm
|
||||
val res = maxProductCutting(n)
|
||||
println("Maximum cutting product is $res")
|
||||
}
|
||||
@@ -0,0 +1,126 @@
|
||||
/**
|
||||
* File: array_hash_map.kt
|
||||
* Created Time: 2024-01-25
|
||||
* Author: curtishd (1023632660@qq.com)
|
||||
*/
|
||||
|
||||
package chapter_hashing
|
||||
|
||||
/* Key-value pair */
|
||||
class Pair(
|
||||
var key: Int,
|
||||
var _val: String
|
||||
)
|
||||
|
||||
/* Hash table based on array implementation */
|
||||
class ArrayHashMap {
|
||||
// Initialize array with 100 buckets
|
||||
private val buckets = arrayOfNulls<Pair>(100)
|
||||
|
||||
/* Hash function */
|
||||
fun hashFunc(key: Int): Int {
|
||||
val index = key % 100
|
||||
return index
|
||||
}
|
||||
|
||||
/* Query operation */
|
||||
fun get(key: Int): String? {
|
||||
val index = hashFunc(key)
|
||||
val pair = buckets[index] ?: return null
|
||||
return pair._val
|
||||
}
|
||||
|
||||
/* Add operation */
|
||||
fun put(key: Int, _val: String) {
|
||||
val pair = Pair(key, _val)
|
||||
val index = hashFunc(key)
|
||||
buckets[index] = pair
|
||||
}
|
||||
|
||||
/* Remove operation */
|
||||
fun remove(key: Int) {
|
||||
val index = hashFunc(key)
|
||||
// Set to null to represent deletion
|
||||
buckets[index] = null
|
||||
}
|
||||
|
||||
/* Get all key-value pairs */
|
||||
fun pairSet(): MutableList<Pair> {
|
||||
val pairSet = mutableListOf<Pair>()
|
||||
for (pair in buckets) {
|
||||
if (pair != null)
|
||||
pairSet.add(pair)
|
||||
}
|
||||
return pairSet
|
||||
}
|
||||
|
||||
/* Get all keys */
|
||||
fun keySet(): MutableList<Int> {
|
||||
val keySet = mutableListOf<Int>()
|
||||
for (pair in buckets) {
|
||||
if (pair != null)
|
||||
keySet.add(pair.key)
|
||||
}
|
||||
return keySet
|
||||
}
|
||||
|
||||
/* Get all values */
|
||||
fun valueSet(): MutableList<String> {
|
||||
val valueSet = mutableListOf<String>()
|
||||
for (pair in buckets) {
|
||||
if (pair != null)
|
||||
valueSet.add(pair._val)
|
||||
}
|
||||
return valueSet
|
||||
}
|
||||
|
||||
/* Print hash table */
|
||||
fun print() {
|
||||
for (kv in pairSet()) {
|
||||
val key = kv.key
|
||||
val _val = kv._val
|
||||
println("$key -> $_val")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Driver Code */
|
||||
fun main() {
|
||||
/* Initialize hash table */
|
||||
val map = ArrayHashMap()
|
||||
|
||||
/* Add operation */
|
||||
// Add key-value pair (key, value) to the hash table
|
||||
map.put(12836, "Xiao Ha")
|
||||
map.put(15937, "Xiao Luo")
|
||||
map.put(16750, "Xiao Suan")
|
||||
map.put(13276, "Xiao Fa")
|
||||
map.put(10583, "Xiao Ya")
|
||||
println("\nAfter adding is complete, hash table is\nKey -> Value")
|
||||
map.print()
|
||||
|
||||
/* Query operation */
|
||||
// Input key into hash table to get value
|
||||
val name = map.get(15937)
|
||||
println("\nInput student ID 15937, found name $name")
|
||||
|
||||
/* Remove operation */
|
||||
// Remove key-value pair (key, value) from hash table
|
||||
map.remove(10583)
|
||||
println("\nAfter removing 10583, hash table is\nKey -> Value")
|
||||
map.print()
|
||||
|
||||
/* Traverse hash table */
|
||||
println("\nTraverse key-value pairs Key -> Value")
|
||||
for (kv in map.pairSet()) {
|
||||
println("${kv.key} -> ${kv._val}")
|
||||
}
|
||||
println("\nTraverse keys only Key")
|
||||
for (key in map.keySet()) {
|
||||
println(key)
|
||||
}
|
||||
println("\nTraverse values only Value")
|
||||
for (_val in map.valueSet()) {
|
||||
println(_val)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
/**
|
||||
* File: built_in_hash.kt
|
||||
* Created Time: 2024-01-25
|
||||
* Author: curtishd (1023632660@qq.com)
|
||||
*/
|
||||
|
||||
package chapter_hashing
|
||||
|
||||
import utils.ListNode
|
||||
|
||||
/* Driver Code */
|
||||
fun main() {
|
||||
val num = 3
|
||||
val hashNum = num.hashCode()
|
||||
println("Hash value of integer $num is $hashNum")
|
||||
|
||||
val bol = true
|
||||
val hashBol = bol.hashCode()
|
||||
println("Hash value of boolean $bol is $hashBol")
|
||||
|
||||
val dec = 3.14159
|
||||
val hashDec = dec.hashCode()
|
||||
println("Hash value of decimal $dec is $hashDec")
|
||||
|
||||
val str = "Hello Algo"
|
||||
val hashStr = str.hashCode()
|
||||
println("Hash value of string $str is $hashStr")
|
||||
|
||||
val arr = arrayOf<Any>(12836, "Xiao Ha")
|
||||
val hashTup = arr.contentHashCode()
|
||||
println("Hash value of array ${arr.contentToString()} is $hashTup")
|
||||
|
||||
val obj = ListNode(0)
|
||||
val hashObj = obj.hashCode()
|
||||
println("Hash value of node object $obj is $hashObj")
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
/**
|
||||
* File: hash_map.kt
|
||||
* Created Time: 2024-01-25
|
||||
* Author: curtishd (1023632660@qq.com)
|
||||
*/
|
||||
|
||||
package chapter_hashing
|
||||
|
||||
import utils.printHashMap
|
||||
|
||||
/* Driver Code */
|
||||
fun main() {
|
||||
/* Initialize hash table */
|
||||
val map = HashMap<Int, String>()
|
||||
|
||||
/* Add operation */
|
||||
// Add key-value pair (key, value) to the hash table
|
||||
map[12836] = "Xiao Ha"
|
||||
map[15937] = "Xiao Luo"
|
||||
map[16750] = "Xiao Suan"
|
||||
map[13276] = "Xiao Fa"
|
||||
map[10583] = "Xiao Ya"
|
||||
println("\nAfter adding is complete, hash table is\nKey -> Value")
|
||||
printHashMap(map)
|
||||
|
||||
/* Query operation */
|
||||
// Input key into hash table to get value
|
||||
val name = map[15937]
|
||||
println("\nInput student ID 15937, found name $name")
|
||||
|
||||
/* Remove operation */
|
||||
// Remove key-value pair (key, value) from hash table
|
||||
map.remove(10583)
|
||||
println("\nAfter removing 10583, hash table is\nKey -> Value")
|
||||
printHashMap(map)
|
||||
|
||||
/* Traverse hash table */
|
||||
println("\nTraverse key-value pairs Key->Value")
|
||||
for ((key, value) in map) {
|
||||
println("$key -> $value")
|
||||
}
|
||||
println("\nTraverse keys only Key")
|
||||
for (key in map.keys) {
|
||||
println(key)
|
||||
}
|
||||
println("\nTraverse values only Value")
|
||||
for (_val in map.values) {
|
||||
println(_val)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,145 @@
|
||||
/**
|
||||
* File: hash_map_chaining.kt
|
||||
* Created Time: 2024-01-25
|
||||
* Author: curtishd (1023632660@qq.com)
|
||||
*/
|
||||
|
||||
package chapter_hashing
|
||||
|
||||
/* Hash table with separate chaining */
|
||||
class HashMapChaining {
|
||||
var size: Int // Number of key-value pairs
|
||||
var capacity: Int // Hash table capacity
|
||||
val loadThres: Double // Load factor threshold for triggering expansion
|
||||
val extendRatio: Int // Expansion multiplier
|
||||
var buckets: MutableList<MutableList<Pair>> // Bucket array
|
||||
|
||||
/* Constructor */
|
||||
init {
|
||||
size = 0
|
||||
capacity = 4
|
||||
loadThres = 2.0 / 3.0
|
||||
extendRatio = 2
|
||||
buckets = mutableListOf()
|
||||
for (i in 0..<capacity) {
|
||||
buckets.add(mutableListOf())
|
||||
}
|
||||
}
|
||||
|
||||
/* Hash function */
|
||||
fun hashFunc(key: Int): Int {
|
||||
return key % capacity
|
||||
}
|
||||
|
||||
/* Load factor */
|
||||
fun loadFactor(): Double {
|
||||
return (size / capacity).toDouble()
|
||||
}
|
||||
|
||||
/* Query operation */
|
||||
fun get(key: Int): String? {
|
||||
val index = hashFunc(key)
|
||||
val bucket = buckets[index]
|
||||
// Traverse bucket, if key is found, return corresponding val
|
||||
for (pair in bucket) {
|
||||
if (pair.key == key) return pair._val
|
||||
}
|
||||
// If key is not found, return null
|
||||
return null
|
||||
}
|
||||
|
||||
/* Add operation */
|
||||
fun put(key: Int, _val: String) {
|
||||
// When load factor exceeds threshold, perform expansion
|
||||
if (loadFactor() > loadThres) {
|
||||
extend()
|
||||
}
|
||||
val index = hashFunc(key)
|
||||
val bucket = buckets[index]
|
||||
// Traverse bucket, if specified key is encountered, update corresponding val and return
|
||||
for (pair in bucket) {
|
||||
if (pair.key == key) {
|
||||
pair._val = _val
|
||||
return
|
||||
}
|
||||
}
|
||||
// If key does not exist, append key-value pair to the end
|
||||
val pair = Pair(key, _val)
|
||||
bucket.add(pair)
|
||||
size++
|
||||
}
|
||||
|
||||
/* Remove operation */
|
||||
fun remove(key: Int) {
|
||||
val index = hashFunc(key)
|
||||
val bucket = buckets[index]
|
||||
// Traverse bucket and remove key-value pair from it
|
||||
for (pair in bucket) {
|
||||
if (pair.key == key) {
|
||||
bucket.remove(pair)
|
||||
size--
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Expand hash table */
|
||||
fun extend() {
|
||||
// Temporarily store the original hash table
|
||||
val bucketsTmp = buckets
|
||||
// Initialize expanded new hash table
|
||||
capacity *= extendRatio
|
||||
// mutablelist has no fixed size
|
||||
buckets = mutableListOf()
|
||||
for (i in 0..<capacity) {
|
||||
buckets.add(mutableListOf())
|
||||
}
|
||||
size = 0
|
||||
// Move key-value pairs from original hash table to new hash table
|
||||
for (bucket in bucketsTmp) {
|
||||
for (pair in bucket) {
|
||||
put(pair.key, pair._val)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Print hash table */
|
||||
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() {
|
||||
/* Initialize hash table */
|
||||
val map = HashMapChaining()
|
||||
|
||||
/* Add operation */
|
||||
// Add key-value pair (key, value) to the hash table
|
||||
map.put(12836, "Xiao Ha")
|
||||
map.put(15937, "Xiao Luo")
|
||||
map.put(16750, "Xiao Suan")
|
||||
map.put(13276, "Xiao Fa")
|
||||
map.put(10583, "Xiao Ya")
|
||||
println("\nAfter adding is complete, hash table is\nKey -> Value")
|
||||
map.print()
|
||||
|
||||
/* Query operation */
|
||||
// Input key into hash table to get value
|
||||
val name = map.get(13276)
|
||||
println("\nInput student ID 13276, found name $name")
|
||||
|
||||
/* Remove operation */
|
||||
// Remove key-value pair (key, value) from hash table
|
||||
map.remove(12836)
|
||||
println("\nAfter removing 12836, hash table is\nKey -> Value")
|
||||
map.print()
|
||||
}
|
||||
@@ -0,0 +1,161 @@
|
||||
/**
|
||||
* File: hash_map_open_addressing.kt
|
||||
* Created Time: 2024-01-25
|
||||
* Author: curtishd (1023632660@qq.com)
|
||||
*/
|
||||
|
||||
package chapter_hashing
|
||||
|
||||
/* Hash table with open addressing */
|
||||
class HashMapOpenAddressing {
|
||||
private var size: Int // Number of key-value pairs
|
||||
private var capacity: Int // Hash table capacity
|
||||
private val loadThres: Double // Load factor threshold for triggering expansion
|
||||
private val extendRatio: Int // Expansion multiplier
|
||||
private var buckets: Array<Pair?> // Bucket array
|
||||
private val TOMBSTONE: Pair // Removal marker
|
||||
|
||||
/* Constructor */
|
||||
init {
|
||||
size = 0
|
||||
capacity = 4
|
||||
loadThres = 2.0 / 3.0
|
||||
extendRatio = 2
|
||||
buckets = arrayOfNulls(capacity)
|
||||
TOMBSTONE = Pair(-1, "-1")
|
||||
}
|
||||
|
||||
/* Hash function */
|
||||
fun hashFunc(key: Int): Int {
|
||||
return key % capacity
|
||||
}
|
||||
|
||||
/* Load factor */
|
||||
fun loadFactor(): Double {
|
||||
return (size / capacity).toDouble()
|
||||
}
|
||||
|
||||
/* Search for bucket index corresponding to key */
|
||||
fun findBucket(key: Int): Int {
|
||||
var index = hashFunc(key)
|
||||
var firstTombstone = -1
|
||||
// Linear probing, break when encountering an empty bucket
|
||||
while (buckets[index] != null) {
|
||||
// If key is encountered, return the corresponding bucket index
|
||||
if (buckets[index]?.key == key) {
|
||||
// If a removal marker was encountered before, move the key-value pair to that index
|
||||
if (firstTombstone != -1) {
|
||||
buckets[firstTombstone] = buckets[index]
|
||||
buckets[index] = TOMBSTONE
|
||||
return firstTombstone // Return the moved bucket index
|
||||
}
|
||||
return index // Return bucket index
|
||||
}
|
||||
// Record the first removal marker encountered
|
||||
if (firstTombstone == -1 && buckets[index] == TOMBSTONE) {
|
||||
firstTombstone = index
|
||||
}
|
||||
// Calculate bucket index, wrap around to the head if past the tail
|
||||
index = (index + 1) % capacity
|
||||
}
|
||||
// If key does not exist, return the index for insertion
|
||||
return if (firstTombstone == -1) index else firstTombstone
|
||||
}
|
||||
|
||||
/* Query operation */
|
||||
fun get(key: Int): String? {
|
||||
// Search for bucket index corresponding to key
|
||||
val index = findBucket(key)
|
||||
// If key-value pair is found, return corresponding val
|
||||
if (buckets[index] != null && buckets[index] != TOMBSTONE) {
|
||||
return buckets[index]?._val
|
||||
}
|
||||
// If key-value pair does not exist, return null
|
||||
return null
|
||||
}
|
||||
|
||||
/* Add operation */
|
||||
fun put(key: Int, _val: String) {
|
||||
// When load factor exceeds threshold, perform expansion
|
||||
if (loadFactor() > loadThres) {
|
||||
extend()
|
||||
}
|
||||
// Search for bucket index corresponding to key
|
||||
val index = findBucket(key)
|
||||
// If key-value pair is found, overwrite val and return
|
||||
if (buckets[index] != null && buckets[index] != TOMBSTONE) {
|
||||
buckets[index]!!._val = _val
|
||||
return
|
||||
}
|
||||
// If key-value pair does not exist, add the key-value pair
|
||||
buckets[index] = Pair(key, _val)
|
||||
size++
|
||||
}
|
||||
|
||||
/* Remove operation */
|
||||
fun remove(key: Int) {
|
||||
// Search for bucket index corresponding to key
|
||||
val index = findBucket(key)
|
||||
// If key-value pair is found, overwrite it with removal marker
|
||||
if (buckets[index] != null && buckets[index] != TOMBSTONE) {
|
||||
buckets[index] = TOMBSTONE
|
||||
size--
|
||||
}
|
||||
}
|
||||
|
||||
/* Expand hash table */
|
||||
fun extend() {
|
||||
// Temporarily store the original hash table
|
||||
val bucketsTmp = buckets
|
||||
// Initialize expanded new hash table
|
||||
capacity *= extendRatio
|
||||
buckets = arrayOfNulls(capacity)
|
||||
size = 0
|
||||
// Move key-value pairs from original hash table to new hash table
|
||||
for (pair in bucketsTmp) {
|
||||
if (pair != null && pair != TOMBSTONE) {
|
||||
put(pair.key, pair._val)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Print hash table */
|
||||
fun print() {
|
||||
for (pair in buckets) {
|
||||
if (pair == null) {
|
||||
println("null")
|
||||
} else if (pair == TOMBSTONE) {
|
||||
println("TOMESTOME")
|
||||
} else {
|
||||
println("${pair.key} -> ${pair._val}")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Driver Code */
|
||||
fun main() {
|
||||
// Initialize hash table
|
||||
val hashmap = HashMapOpenAddressing()
|
||||
|
||||
// Add operation
|
||||
// Add key-value pair (key, val) to the hash table
|
||||
hashmap.put(12836, "Xiao Ha")
|
||||
hashmap.put(15937, "Xiao Luo")
|
||||
hashmap.put(16750, "Xiao Suan")
|
||||
hashmap.put(13276, "Xiao Fa")
|
||||
hashmap.put(10583, "Xiao Ya")
|
||||
println("\nAfter adding is complete, hash table is\nKey -> Value")
|
||||
hashmap.print()
|
||||
|
||||
// Query operation
|
||||
// Input key into hash table to get value val
|
||||
val name = hashmap.get(13276)
|
||||
println("\nInput student ID 13276, found name $name")
|
||||
|
||||
// Remove operation
|
||||
// Remove key-value pair (key, val) from hash table
|
||||
hashmap.remove(16750)
|
||||
println("\nAfter removing 16750, hash table is\nKey -> Value")
|
||||
hashmap.print()
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
/**
|
||||
* File: simple_hash.kt
|
||||
* Created Time: 2024-01-25
|
||||
* Author: curtishd (1023632660@qq.com)
|
||||
*/
|
||||
|
||||
package chapter_hashing
|
||||
|
||||
/* Additive hash */
|
||||
fun addHash(key: String): Int {
|
||||
var hash = 0L
|
||||
val MODULUS = 1000000007
|
||||
for (c in key.toCharArray()) {
|
||||
hash = (hash + c.code) % MODULUS
|
||||
}
|
||||
return hash.toInt()
|
||||
}
|
||||
|
||||
/* Multiplicative hash */
|
||||
fun mulHash(key: String): Int {
|
||||
var hash = 0L
|
||||
val MODULUS = 1000000007
|
||||
for (c in key.toCharArray()) {
|
||||
hash = (31 * hash + c.code) % MODULUS
|
||||
}
|
||||
return hash.toInt()
|
||||
}
|
||||
|
||||
/* XOR hash */
|
||||
fun xorHash(key: String): Int {
|
||||
var hash = 0
|
||||
val MODULUS = 1000000007
|
||||
for (c in key.toCharArray()) {
|
||||
hash = hash xor c.code
|
||||
}
|
||||
return hash and MODULUS
|
||||
}
|
||||
|
||||
/* Rotational hash */
|
||||
fun rotHash(key: String): Int {
|
||||
var hash = 0L
|
||||
val MODULUS = 1000000007
|
||||
for (c in key.toCharArray()) {
|
||||
hash = ((hash shl 4) xor (hash shr 28) xor c.code.toLong()) % MODULUS
|
||||
}
|
||||
return hash.toInt()
|
||||
}
|
||||
|
||||
/* Driver Code */
|
||||
fun main() {
|
||||
val key = "Hello Algo"
|
||||
|
||||
var hash = addHash(key)
|
||||
println("Additive hash value is $hash")
|
||||
|
||||
hash = mulHash(key)
|
||||
println("Multiplicative hash value is $hash")
|
||||
|
||||
hash = xorHash(key)
|
||||
println("XOR hash value is $hash")
|
||||
|
||||
hash = rotHash(key)
|
||||
println("Rotational hash value is $hash")
|
||||
}
|
||||
@@ -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) // Element enters heap
|
||||
print("\nAfter element $_val pushes to heap\n")
|
||||
printHeap(heap)
|
||||
}
|
||||
|
||||
fun testPop(heap: Queue<Int>) {
|
||||
val _val = heap.poll() // Time complexity is O(n), not O(nlogn)
|
||||
print("\nAfter heap top element $_val pops from heap\n")
|
||||
printHeap(heap)
|
||||
}
|
||||
|
||||
/* Driver Code */
|
||||
fun main() {
|
||||
/* Initialize heap */
|
||||
// Python's heapq module implements min heap by default
|
||||
var minHeap = PriorityQueue<Int>()
|
||||
|
||||
// Initialize max heap (modify Comparator using lambda expression)
|
||||
val maxHeap = PriorityQueue { a: Int, b: Int -> b - a }
|
||||
|
||||
println("\nThe following test cases are for max heap")
|
||||
|
||||
/* Element enters heap */
|
||||
testPush(maxHeap, 1)
|
||||
testPush(maxHeap, 3)
|
||||
testPush(maxHeap, 2)
|
||||
testPush(maxHeap, 5)
|
||||
testPush(maxHeap, 4)
|
||||
|
||||
/* Check if heap is empty */
|
||||
val peek = maxHeap.peek()
|
||||
print("\nHeap top element is $peek\n")
|
||||
|
||||
/* Time complexity is O(n), not O(nlogn) */
|
||||
testPop(maxHeap)
|
||||
testPop(maxHeap)
|
||||
testPop(maxHeap)
|
||||
testPop(maxHeap)
|
||||
testPop(maxHeap)
|
||||
|
||||
/* Get heap size */
|
||||
val size = maxHeap.size
|
||||
print("\nHeap size is $size\n")
|
||||
|
||||
/* Check if heap is empty */
|
||||
val isEmpty = maxHeap.isEmpty()
|
||||
print("\nIs heap empty $isEmpty\n")
|
||||
|
||||
/* Input list and build heap */
|
||||
// Time complexity is O(n), not O(nlogn)
|
||||
minHeap = PriorityQueue(mutableListOf<Int?>(1, 3, 2, 5, 4))
|
||||
println("\nAfter inputting list and building min heap")
|
||||
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.*
|
||||
|
||||
/* Max heap */
|
||||
class MaxHeap(nums: MutableList<Int>?) {
|
||||
// Use list instead of array, no need to consider capacity expansion
|
||||
private val maxHeap = mutableListOf<Int>()
|
||||
|
||||
/* Constructor, build heap based on input list */
|
||||
init {
|
||||
// Add list elements to heap as is
|
||||
maxHeap.addAll(nums!!)
|
||||
// Heapify all nodes except leaf nodes
|
||||
for (i in parent(size() - 1) downTo 0) {
|
||||
siftDown(i)
|
||||
}
|
||||
}
|
||||
|
||||
/* Get index of left child node */
|
||||
private fun left(i: Int): Int {
|
||||
return 2 * i + 1
|
||||
}
|
||||
|
||||
/* Get index of right child node */
|
||||
private fun right(i: Int): Int {
|
||||
return 2 * i + 2
|
||||
}
|
||||
|
||||
/* Get index of parent node */
|
||||
private fun parent(i: Int): Int {
|
||||
return (i - 1) / 2 // Floor division
|
||||
}
|
||||
|
||||
/* Swap elements */
|
||||
private fun swap(i: Int, j: Int) {
|
||||
val temp = maxHeap[i]
|
||||
maxHeap[i] = maxHeap[j]
|
||||
maxHeap[j] = temp
|
||||
}
|
||||
|
||||
/* Get heap size */
|
||||
fun size(): Int {
|
||||
return maxHeap.size
|
||||
}
|
||||
|
||||
/* Check if heap is empty */
|
||||
fun isEmpty(): Boolean {
|
||||
/* Check if heap is empty */
|
||||
return size() == 0
|
||||
}
|
||||
|
||||
/* Access top element */
|
||||
fun peek(): Int {
|
||||
return maxHeap[0]
|
||||
}
|
||||
|
||||
/* Element enters heap */
|
||||
fun push(_val: Int) {
|
||||
// Add node
|
||||
maxHeap.add(_val)
|
||||
// Heapify from bottom to top
|
||||
siftUp(size() - 1)
|
||||
}
|
||||
|
||||
/* Starting from node i, heapify from bottom to top */
|
||||
private fun siftUp(it: Int) {
|
||||
// Kotlin function parameters are immutable, so create temporary variable
|
||||
var i = it
|
||||
while (true) {
|
||||
// Get parent node of node i
|
||||
val p = parent(i)
|
||||
// When "crossing root node" or "node needs no repair", end heapify
|
||||
if (p < 0 || maxHeap[i] <= maxHeap[p]) break
|
||||
// Swap two nodes
|
||||
swap(i, p)
|
||||
// Loop upward heapify
|
||||
i = p
|
||||
}
|
||||
}
|
||||
|
||||
/* Element exits heap */
|
||||
fun pop(): Int {
|
||||
// Handle empty case
|
||||
if (isEmpty()) throw IndexOutOfBoundsException()
|
||||
// Delete node
|
||||
swap(0, size() - 1)
|
||||
// Remove node
|
||||
val _val = maxHeap.removeAt(size() - 1)
|
||||
// Return top element
|
||||
siftDown(0)
|
||||
// Return heap top element
|
||||
return _val
|
||||
}
|
||||
|
||||
/* Starting from node i, heapify from top to bottom */
|
||||
private fun siftDown(it: Int) {
|
||||
// Kotlin function parameters are immutable, so create temporary variable
|
||||
var i = it
|
||||
while (true) {
|
||||
// If node i is largest or indices l, r are out of bounds, no need to continue heapify, break
|
||||
val l = left(i)
|
||||
val r = right(i)
|
||||
var ma = i
|
||||
if (l < size() && maxHeap[l] > maxHeap[ma]) ma = l
|
||||
if (r < size() && maxHeap[r] > maxHeap[ma]) ma = r
|
||||
// Swap two nodes
|
||||
if (ma == i) break
|
||||
// Swap two nodes
|
||||
swap(i, ma)
|
||||
// Loop downwards heapification
|
||||
i = ma
|
||||
}
|
||||
}
|
||||
|
||||
/* Driver Code */
|
||||
fun print() {
|
||||
val queue = PriorityQueue { a: Int, b: Int -> b - a }
|
||||
queue.addAll(maxHeap)
|
||||
printHeap(queue)
|
||||
}
|
||||
}
|
||||
|
||||
/* Driver Code */
|
||||
fun main() {
|
||||
/* Consider negating the elements before entering the heap, which can reverse the size relationship, thus implementing max heap */
|
||||
val maxHeap = MaxHeap(mutableListOf(9, 8, 6, 6, 7, 5, 2, 1, 4, 3, 6, 2))
|
||||
println("\nAfter inputting list and building heap")
|
||||
maxHeap.print()
|
||||
|
||||
/* Check if heap is empty */
|
||||
var peek = maxHeap.peek()
|
||||
print("\nHeap top element is $peek\n")
|
||||
|
||||
/* Element enters heap */
|
||||
val _val = 7
|
||||
maxHeap.push(_val)
|
||||
print("\nAfter element $_val pushes to heap\n")
|
||||
maxHeap.print()
|
||||
|
||||
/* Time complexity is O(n), not O(nlogn) */
|
||||
peek = maxHeap.pop()
|
||||
print("\nAfter heap top element $peek pops from heap\n")
|
||||
maxHeap.print()
|
||||
|
||||
/* Get heap size */
|
||||
val size = maxHeap.size()
|
||||
print("\nHeap size is $size\n")
|
||||
|
||||
/* Check if heap is empty */
|
||||
val isEmpty = maxHeap.isEmpty()
|
||||
print("\nIs heap empty $isEmpty\n")
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
/**
|
||||
* File: top_k.kt
|
||||
* Created Time: 2024-01-25
|
||||
* Author: curtishd (1023632660@qq.com)
|
||||
*/
|
||||
|
||||
package chapter_heap
|
||||
|
||||
import utils.printHeap
|
||||
import java.util.*
|
||||
|
||||
/* Find the largest k elements in array based on heap */
|
||||
fun topKHeap(nums: IntArray, k: Int): Queue<Int> {
|
||||
// Python's heapq module implements min heap by default
|
||||
val heap = PriorityQueue<Int>()
|
||||
// Enter the first k elements of array into heap
|
||||
for (i in 0..<k) {
|
||||
heap.offer(nums[i])
|
||||
}
|
||||
// Starting from the (k+1)th element, maintain heap length as k
|
||||
for (i in k..<nums.size) {
|
||||
// If current element is greater than top element, top element exits heap, current element enters heap
|
||||
if (nums[i] > heap.peek()) {
|
||||
heap.poll()
|
||||
heap.offer(nums[i])
|
||||
}
|
||||
}
|
||||
return heap
|
||||
}
|
||||
|
||||
/* Driver Code */
|
||||
fun main() {
|
||||
val nums = intArrayOf(1, 7, 6, 3, 2)
|
||||
val k = 3
|
||||
val res = topKHeap(nums, k)
|
||||
println("The largest $k elements are")
|
||||
printHeap(res)
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
/**
|
||||
* File: binary_search.kt
|
||||
* Created Time: 2024-01-25
|
||||
* Author: curtishd (1023632660@qq.com)
|
||||
*/
|
||||
|
||||
package chapter_searching
|
||||
|
||||
/* Binary search (closed interval on both sides) */
|
||||
fun binarySearch(nums: IntArray, target: Int): Int {
|
||||
// Initialize closed interval [0, n-1], i.e., i, j point to the first and last elements of the array
|
||||
var i = 0
|
||||
var j = nums.size - 1
|
||||
// Loop, exit when the search interval is empty (empty when i > j)
|
||||
while (i <= j) {
|
||||
val m = i + (j - i) / 2 // Calculate the midpoint index m
|
||||
if (nums[m] < target) // This means target is in the interval [m+1, j]
|
||||
i = m + 1
|
||||
else if (nums[m] > target) // This means target is in the interval [i, m-1]
|
||||
j = m - 1
|
||||
else // Found the target element, return its index
|
||||
return m
|
||||
}
|
||||
// Target element not found, return -1
|
||||
return -1
|
||||
}
|
||||
|
||||
/* Binary search (left-closed right-open interval) */
|
||||
fun binarySearchLCRO(nums: IntArray, target: Int): Int {
|
||||
// Initialize left-closed right-open interval [0, n), i.e., i, j point to the first element and last element+1
|
||||
var i = 0
|
||||
var j = nums.size
|
||||
// Loop, exit when the search interval is empty (empty when i = j)
|
||||
while (i < j) {
|
||||
val m = i + (j - i) / 2 // Calculate the midpoint index m
|
||||
if (nums[m] < target) // This means target is in the interval [m+1, j)
|
||||
i = m + 1
|
||||
else if (nums[m] > target) // This means target is in the interval [i, m)
|
||||
j = m
|
||||
else // Found the target element, return its index
|
||||
return m
|
||||
}
|
||||
// Target element not found, return -1
|
||||
return -1
|
||||
}
|
||||
|
||||
/* Driver Code */
|
||||
fun main() {
|
||||
val target = 6
|
||||
val nums = intArrayOf(1, 3, 6, 8, 12, 15, 23, 26, 31, 35)
|
||||
|
||||
/* Binary search (closed interval on both sides) */
|
||||
var index = binarySearch(nums, target)
|
||||
println("Index of target element 6 = $index")
|
||||
|
||||
/* Binary search (left-closed right-open interval) */
|
||||
index = binarySearchLCRO(nums, target)
|
||||
println("Index of target element 6 = $index")
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
/**
|
||||
* File: binary_search_edge.kt
|
||||
* Created Time: 2024-01-25
|
||||
* Author: curtishd (1023632660@qq.com)
|
||||
*/
|
||||
|
||||
package chapter_searching
|
||||
|
||||
/* Binary search for the leftmost target */
|
||||
fun binarySearchLeftEdge(nums: IntArray, target: Int): Int {
|
||||
// Equivalent to finding the insertion point of target
|
||||
val i = binarySearchInsertion(nums, target)
|
||||
// Target not found, return -1
|
||||
if (i == nums.size || nums[i] != target) {
|
||||
return -1
|
||||
}
|
||||
// Found target, return index i
|
||||
return i
|
||||
}
|
||||
|
||||
/* Binary search for the rightmost target */
|
||||
fun binarySearchRightEdge(nums: IntArray, target: Int): Int {
|
||||
// Convert to finding the leftmost target + 1
|
||||
val i = binarySearchInsertion(nums, target + 1)
|
||||
// j points to the rightmost target, i points to the first element greater than target
|
||||
val j = i - 1
|
||||
// Target not found, return -1
|
||||
if (j == -1 || nums[j] != target) {
|
||||
return -1
|
||||
}
|
||||
// Found target, return index j
|
||||
return j
|
||||
}
|
||||
|
||||
/* Driver Code */
|
||||
fun main() {
|
||||
// Array with duplicate elements
|
||||
val nums = intArrayOf(1, 3, 6, 6, 6, 6, 6, 10, 12, 15)
|
||||
println("\nArray nums = ${nums.contentToString()}")
|
||||
|
||||
// Binary search left and right boundaries
|
||||
for (target in intArrayOf(6, 7)) {
|
||||
var index = binarySearchLeftEdge(nums, target)
|
||||
println("Leftmost element $target index is $index")
|
||||
index = binarySearchRightEdge(nums, target)
|
||||
println("Rightmost element $target index is $index")
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
/**
|
||||
* File: binary_search_insertion.kt
|
||||
* Created Time: 2024-01-25
|
||||
* Author: curtishd (1023632660@qq.com)
|
||||
*/
|
||||
|
||||
package chapter_searching
|
||||
|
||||
/* Binary search for insertion point (no duplicate elements) */
|
||||
fun binarySearchInsertionSimple(nums: IntArray, target: Int): Int {
|
||||
var i = 0
|
||||
var j = nums.size - 1 // Initialize closed interval [0, n-1]
|
||||
while (i <= j) {
|
||||
val m = i + (j - i) / 2 // Calculate the midpoint index m
|
||||
if (nums[m] < target) {
|
||||
i = m + 1 // target is in the interval [m+1, j]
|
||||
} else if (nums[m] > target) {
|
||||
j = m - 1 // target is in the interval [i, m-1]
|
||||
} else {
|
||||
return m // Found target, return insertion point m
|
||||
}
|
||||
}
|
||||
// Target not found, return insertion point i
|
||||
return i
|
||||
}
|
||||
|
||||
/* Binary search for insertion point (with duplicate elements) */
|
||||
fun binarySearchInsertion(nums: IntArray, target: Int): Int {
|
||||
var i = 0
|
||||
var j = nums.size - 1 // Initialize closed interval [0, n-1]
|
||||
while (i <= j) {
|
||||
val m = i + (j - i) / 2 // Calculate the midpoint index m
|
||||
if (nums[m] < target) {
|
||||
i = m + 1 // target is in the interval [m+1, j]
|
||||
} else if (nums[m] > target) {
|
||||
j = m - 1 // target is in the interval [i, m-1]
|
||||
} else {
|
||||
j = m - 1 // The first element less than target is in the interval [i, m-1]
|
||||
}
|
||||
}
|
||||
// Return insertion point i
|
||||
return i
|
||||
}
|
||||
|
||||
/* Driver Code */
|
||||
fun main() {
|
||||
// Array without duplicate elements
|
||||
var nums = intArrayOf(1, 3, 6, 8, 12, 15, 23, 26, 31, 35)
|
||||
println("\nArray nums = ${nums.contentToString()}")
|
||||
// Binary search for insertion point
|
||||
for (target in intArrayOf(6, 9)) {
|
||||
val index = binarySearchInsertionSimple(nums, target)
|
||||
println("Insertion point index for element $target is $index")
|
||||
}
|
||||
|
||||
// Array with duplicate elements
|
||||
nums = intArrayOf(1, 3, 6, 6, 6, 6, 6, 10, 12, 15)
|
||||
println("\nArray nums = ${nums.contentToString()}")
|
||||
|
||||
// Binary search for insertion point
|
||||
for (target in intArrayOf(2, 6, 20)) {
|
||||
val index = binarySearchInsertion(nums, target)
|
||||
println("Insertion point index for element $target is $index")
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
/**
|
||||
* File: hashing_search.kt
|
||||
* Created Time: 2024-01-25
|
||||
* Author: curtishd (1023632660@qq.com)
|
||||
*/
|
||||
|
||||
package chapter_searching
|
||||
|
||||
import utils.ListNode
|
||||
|
||||
/* Hash search (array) */
|
||||
fun hashingSearchArray(map: Map<Int?, Int>, target: Int): Int {
|
||||
// Hash table key: target element, _val: index
|
||||
// If this key does not exist in the hash table, return -1
|
||||
return map.getOrDefault(target, -1)
|
||||
}
|
||||
|
||||
/* Hash search (linked list) */
|
||||
fun hashingSearchLinkedList(map: Map<Int?, ListNode?>, target: Int): ListNode? {
|
||||
// Hash table key: target node value, _val: node object
|
||||
// If key is not in hash table, return null
|
||||
return map.getOrDefault(target, null)
|
||||
}
|
||||
|
||||
/* Driver Code */
|
||||
fun main() {
|
||||
val target = 3
|
||||
|
||||
/* Hash search (array) */
|
||||
val nums = intArrayOf(1, 5, 3, 2, 4, 7, 5, 9, 10, 8)
|
||||
// Initialize hash table
|
||||
val map = HashMap<Int?, Int>()
|
||||
for (i in nums.indices) {
|
||||
map[nums[i]] = i // key: element, _val: index
|
||||
}
|
||||
val index = hashingSearchArray(map, target)
|
||||
println("Index of target element 3 = $index")
|
||||
|
||||
/* Hash search (linked list) */
|
||||
var head = ListNode.arrToLinkedList(nums)
|
||||
// Initialize hash table
|
||||
val map1 = HashMap<Int?, ListNode?>()
|
||||
while (head != null) {
|
||||
map1[head._val] = head // key: node value, _val: node
|
||||
head = head.next
|
||||
}
|
||||
val node = hashingSearchLinkedList(map1, target)
|
||||
println("Node object corresponding to target node value 3 is $node")
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
/**
|
||||
* File: linear_search.kt
|
||||
* Created Time: 2024-01-25
|
||||
* Author: curtishd (1023632660@qq.com)
|
||||
*/
|
||||
|
||||
package chapter_searching
|
||||
|
||||
import utils.ListNode
|
||||
|
||||
/* Linear search (array) */
|
||||
fun linearSearchArray(nums: IntArray, target: Int): Int {
|
||||
// Traverse array
|
||||
for (i in nums.indices) {
|
||||
// Found the target element, return its index
|
||||
if (nums[i] == target)
|
||||
return i
|
||||
}
|
||||
// Target element not found, return -1
|
||||
return -1
|
||||
}
|
||||
|
||||
/* Linear search (linked list) */
|
||||
fun linearSearchLinkedList(h: ListNode?, target: Int): ListNode? {
|
||||
// Traverse the linked list
|
||||
var head = h
|
||||
while (head != null) {
|
||||
// Found the target node, return it
|
||||
if (head._val == target)
|
||||
return head
|
||||
head = head.next
|
||||
}
|
||||
// Target node not found, return null
|
||||
return null
|
||||
}
|
||||
|
||||
/* Driver Code */
|
||||
fun main() {
|
||||
val target = 3
|
||||
|
||||
/* Perform linear search in array */
|
||||
val nums = intArrayOf(1, 5, 3, 2, 4, 7, 5, 9, 10, 8)
|
||||
val index = linearSearchArray(nums, target)
|
||||
println("Index of target element 3 = $index")
|
||||
|
||||
/* Perform linear search in linked list */
|
||||
val head = ListNode.arrToLinkedList(nums)
|
||||
val node = linearSearchLinkedList(head, target)
|
||||
println("Node object corresponding to target node value 3 is $node")
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
/**
|
||||
* File: two_sum.kt
|
||||
* Created Time: 2024-01-25
|
||||
* Author: curtishd (1023632660@qq.com)
|
||||
*/
|
||||
|
||||
package chapter_searching
|
||||
|
||||
/* Method 1: Brute force enumeration */
|
||||
fun twoSumBruteForce(nums: IntArray, target: Int): IntArray {
|
||||
val size = nums.size
|
||||
// Two nested loops, time complexity is O(n^2)
|
||||
for (i in 0..<size - 1) {
|
||||
for (j in i + 1..<size) {
|
||||
if (nums[i] + nums[j] == target) return intArrayOf(i, j)
|
||||
}
|
||||
}
|
||||
return IntArray(0)
|
||||
}
|
||||
|
||||
/* Method 2: Auxiliary hash table */
|
||||
fun twoSumHashTable(nums: IntArray, target: Int): IntArray {
|
||||
val size = nums.size
|
||||
// Auxiliary hash table, space complexity is O(n)
|
||||
val dic = HashMap<Int, Int>()
|
||||
// Single loop, time complexity is 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 ======
|
||||
// Method 1
|
||||
var res = twoSumBruteForce(nums, target)
|
||||
println("Method 1 res = ${res.contentToString()}")
|
||||
// Method 2
|
||||
res = twoSumHashTable(nums, target)
|
||||
println("Method 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
|
||||
|
||||
/* Bubble sort */
|
||||
fun bubbleSort(nums: IntArray) {
|
||||
// Outer loop: unsorted range is [0, i]
|
||||
for (i in nums.size - 1 downTo 1) {
|
||||
// Inner loop: swap the largest element in the unsorted range [0, i] to the rightmost end of that range
|
||||
for (j in 0..<i) {
|
||||
if (nums[j] > nums[j + 1]) {
|
||||
// Swap nums[j] and nums[j + 1]
|
||||
val temp = nums[j]
|
||||
nums[j] = nums[j + 1]
|
||||
nums[j + 1] = temp
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Bubble sort (flag optimization) */
|
||||
fun bubbleSortWithFlag(nums: IntArray) {
|
||||
// Outer loop: unsorted range is [0, i]
|
||||
for (i in nums.size - 1 downTo 1) {
|
||||
var flag = false // Initialize flag
|
||||
// Inner loop: swap the largest element in the unsorted range [0, i] to the rightmost end of that range
|
||||
for (j in 0..<i) {
|
||||
if (nums[j] > nums[j + 1]) {
|
||||
// Swap nums[j] and nums[j + 1]
|
||||
val temp = nums[j]
|
||||
nums[j] = nums[j + 1]
|
||||
nums[j + 1] = temp
|
||||
flag = true // Record element swap
|
||||
}
|
||||
}
|
||||
if (!flag) break // No elements were swapped in this round of "bubbling", exit directly
|
||||
}
|
||||
}
|
||||
|
||||
/* Driver Code */
|
||||
fun main() {
|
||||
val nums = intArrayOf(4, 1, 3, 1, 5, 2)
|
||||
bubbleSort(nums)
|
||||
println("After bubble sort, nums = ${nums.contentToString()}")
|
||||
|
||||
val nums1 = intArrayOf(4, 1, 3, 1, 5, 2)
|
||||
bubbleSortWithFlag(nums1)
|
||||
println("After bubble sort, nums1 = ${nums1.contentToString()}")
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
/**
|
||||
* File: bucket_sort.kt
|
||||
* Created Time: 2024-01-25
|
||||
* Author: curtishd (1023632660@qq.com)
|
||||
*/
|
||||
|
||||
package chapter_sorting
|
||||
|
||||
/* Bucket sort */
|
||||
fun bucketSort(nums: FloatArray) {
|
||||
// Initialize k = n/2 buckets, expected to allocate 2 elements per bucket
|
||||
val k = nums.size / 2
|
||||
val buckets = mutableListOf<MutableList<Float>>()
|
||||
for (i in 0..<k) {
|
||||
buckets.add(mutableListOf())
|
||||
}
|
||||
// 1. Distribute array elements into various buckets
|
||||
for (num in nums) {
|
||||
// Input data range is [0, 1), use num * k to map to index range [0, k-1]
|
||||
val i = (num * k).toInt()
|
||||
// Add num to bucket i
|
||||
buckets[i].add(num)
|
||||
}
|
||||
// 2. Sort each bucket
|
||||
for (bucket in buckets) {
|
||||
// Use built-in sorting function, can also replace with other sorting algorithms
|
||||
bucket.sort()
|
||||
}
|
||||
// 3. Traverse buckets to merge results
|
||||
var i = 0
|
||||
for (bucket in buckets) {
|
||||
for (num in bucket) {
|
||||
nums[i++] = num
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Driver Code */
|
||||
fun main() {
|
||||
// Assume input data is floating point, interval [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("After bucket sort, 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
|
||||
|
||||
/* Counting sort */
|
||||
// Simple implementation, cannot be used for sorting objects
|
||||
fun countingSortNaive(nums: IntArray) {
|
||||
// 1. Count the maximum element m in the array
|
||||
var m = 0
|
||||
for (num in nums) {
|
||||
m = max(m, num)
|
||||
}
|
||||
// 2. Count the occurrence of each number
|
||||
// counter[num] represents the occurrence of num
|
||||
val counter = IntArray(m + 1)
|
||||
for (num in nums) {
|
||||
counter[num]++
|
||||
}
|
||||
// 3. Traverse counter, filling each element back into the original array nums
|
||||
var i = 0
|
||||
for (num in 0..<m + 1) {
|
||||
var j = 0
|
||||
while (j < counter[num]) {
|
||||
nums[i] = num
|
||||
j++
|
||||
i++
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Counting sort */
|
||||
// Complete implementation, can sort objects and is a stable sort
|
||||
fun countingSort(nums: IntArray) {
|
||||
// 1. Count the maximum element m in the array
|
||||
var m = 0
|
||||
for (num in nums) {
|
||||
m = max(m, num)
|
||||
}
|
||||
// 2. Count the occurrence of each number
|
||||
// counter[num] represents the occurrence of num
|
||||
val counter = IntArray(m + 1)
|
||||
for (num in nums) {
|
||||
counter[num]++
|
||||
}
|
||||
// 3. Calculate the prefix sum of counter, converting "occurrence count" to "tail index"
|
||||
// counter[num]-1 is the last index where num appears in res
|
||||
for (i in 0..<m) {
|
||||
counter[i + 1] += counter[i]
|
||||
}
|
||||
// 4. Traverse nums in reverse order, placing each element into the result array res
|
||||
// Initialize the array res to record results
|
||||
val n = nums.size
|
||||
val res = IntArray(n)
|
||||
for (i in n - 1 downTo 0) {
|
||||
val num = nums[i]
|
||||
res[counter[num] - 1] = num // Place num at the corresponding index
|
||||
counter[num]-- // Decrement the prefix sum by 1, getting the next index to place num
|
||||
}
|
||||
// Use result array res to overwrite the original array nums
|
||||
for (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("After counting sort (cannot sort objects), nums = ${nums.contentToString()}")
|
||||
|
||||
val nums1 = intArrayOf(1, 0, 1, 2, 0, 4, 0, 2, 2, 4)
|
||||
countingSort(nums1)
|
||||
println("After counting sort, nums1 = ${nums1.contentToString()}")
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
/**
|
||||
* File: heap_sort.kt
|
||||
* Created Time: 2024-01-25
|
||||
* Author: curtishd (1023632660@qq.com)
|
||||
*/
|
||||
|
||||
package chapter_sorting
|
||||
|
||||
/* Heap length is n, start heapifying node i, from top to bottom */
|
||||
fun siftDown(nums: IntArray, n: Int, li: Int) {
|
||||
var i = li
|
||||
while (true) {
|
||||
// If node i is largest or indices l, r are out of bounds, no need to continue heapify, break
|
||||
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
|
||||
// Swap two nodes
|
||||
if (ma == i)
|
||||
break
|
||||
// Swap two nodes
|
||||
val temp = nums[i]
|
||||
nums[i] = nums[ma]
|
||||
nums[ma] = temp
|
||||
// Loop downwards heapification
|
||||
i = ma
|
||||
}
|
||||
}
|
||||
|
||||
/* Heap sort */
|
||||
fun heapSort(nums: IntArray) {
|
||||
// Build heap operation: heapify all nodes except leaves
|
||||
for (i in nums.size / 2 - 1 downTo 0) {
|
||||
siftDown(nums, nums.size, i)
|
||||
}
|
||||
// Extract the largest element from the heap and repeat for n-1 rounds
|
||||
for (i in nums.size - 1 downTo 1) {
|
||||
// Delete node
|
||||
val temp = nums[0]
|
||||
nums[0] = nums[i]
|
||||
nums[i] = temp
|
||||
// Start heapifying the root node, from top to bottom
|
||||
siftDown(nums, i, 0)
|
||||
}
|
||||
}
|
||||
|
||||
/* Driver Code */
|
||||
fun main() {
|
||||
val nums = intArrayOf(4, 1, 3, 1, 5, 2)
|
||||
heapSort(nums)
|
||||
println("After heap sort, nums = ${nums.contentToString()}")
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
/**
|
||||
* File: insertion_sort.kt
|
||||
* Created Time: 2024-01-25
|
||||
* Author: curtishd (1023632660@qq.com)
|
||||
*/
|
||||
|
||||
package chapter_sorting
|
||||
|
||||
/* Insertion sort */
|
||||
fun insertionSort(nums: IntArray) {
|
||||
// Outer loop: sorted elements are 1, 2, ..., n
|
||||
for (i in nums.indices) {
|
||||
val base = nums[i]
|
||||
var j = i - 1
|
||||
// Inner loop: insert base into the correct position within the sorted interval [0, i-1]
|
||||
while (j >= 0 && nums[j] > base) {
|
||||
nums[j + 1] = nums[j] // Move nums[j] to the right by one position
|
||||
j--
|
||||
}
|
||||
nums[j + 1] = base // Assign base to the correct position
|
||||
}
|
||||
}
|
||||
|
||||
/* Driver Code */
|
||||
fun main() {
|
||||
val nums = intArrayOf(4, 1, 3, 1, 5, 2)
|
||||
insertionSort(nums)
|
||||
println("After insertion sort, nums = ${nums.contentToString()}")
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
/**
|
||||
* File: merge_sort.kt
|
||||
* Created Time: 2024-01-25
|
||||
* Author: curtishd (1023632660@qq.com)
|
||||
*/
|
||||
|
||||
package chapter_sorting
|
||||
|
||||
/* Merge left subarray and right subarray */
|
||||
fun merge(nums: IntArray, left: Int, mid: Int, right: Int) {
|
||||
// Left subarray interval is [left, mid], right subarray interval is [mid+1, right]
|
||||
// Create a temporary array tmp to store the merged results
|
||||
val tmp = IntArray(right - left + 1)
|
||||
// Initialize the start indices of the left and right subarrays
|
||||
var i = left
|
||||
var j = mid + 1
|
||||
var k = 0
|
||||
// While both subarrays still have elements, compare and copy the smaller element into the temporary array
|
||||
while (i <= mid && j <= right) {
|
||||
if (nums[i] <= nums[j])
|
||||
tmp[k++] = nums[i++]
|
||||
else
|
||||
tmp[k++] = nums[j++]
|
||||
}
|
||||
// Copy the remaining elements of the left and right subarrays into the temporary array
|
||||
while (i <= mid) {
|
||||
tmp[k++] = nums[i++]
|
||||
}
|
||||
while (j <= right) {
|
||||
tmp[k++] = nums[j++]
|
||||
}
|
||||
// Copy the elements from the temporary array tmp back to the original array nums at the corresponding interval
|
||||
for (l in tmp.indices) {
|
||||
nums[left + l] = tmp[l]
|
||||
}
|
||||
}
|
||||
|
||||
/* Merge sort */
|
||||
fun mergeSort(nums: IntArray, left: Int, right: Int) {
|
||||
// Termination condition
|
||||
if (left >= right) return // Terminate recursion when subarray length is 1
|
||||
// Divide and conquer stage
|
||||
val mid = left + (right - left) / 2 // Calculate midpoint
|
||||
mergeSort(nums, left, mid) // Recursively process the left subarray
|
||||
mergeSort(nums, mid + 1, right) // Recursively process the right subarray
|
||||
// Merge stage
|
||||
merge(nums, left, mid, right)
|
||||
}
|
||||
|
||||
/* Driver Code */
|
||||
fun main() {
|
||||
/* Merge sort */
|
||||
val nums = intArrayOf(7, 3, 2, 6, 0, 1, 5, 4)
|
||||
mergeSort(nums, 0, nums.size - 1)
|
||||
println("After merge sort, nums = ${nums.contentToString()}")
|
||||
}
|
||||
@@ -0,0 +1,121 @@
|
||||
/**
|
||||
* File: quick_sort.kt
|
||||
* Created Time: 2024-01-25
|
||||
* Author: curtishd (1023632660@qq.com)
|
||||
*/
|
||||
|
||||
package chapter_sorting
|
||||
|
||||
/* Swap elements */
|
||||
fun swap(nums: IntArray, i: Int, j: Int) {
|
||||
val temp = nums[i]
|
||||
nums[i] = nums[j]
|
||||
nums[j] = temp
|
||||
}
|
||||
|
||||
/* Sentinel partition */
|
||||
fun partition(nums: IntArray, left: Int, right: Int): Int {
|
||||
// Use nums[left] as the pivot
|
||||
var i = left
|
||||
var j = right
|
||||
while (i < j) {
|
||||
while (i < j && nums[j] >= nums[left])
|
||||
j-- // Search from right to left for the first element smaller than the pivot
|
||||
while (i < j && nums[i] <= nums[left])
|
||||
i++ // Search from left to right for the first element greater than the pivot
|
||||
swap(nums, i, j) // Swap these two elements
|
||||
}
|
||||
swap(nums, i, left) // Swap the pivot to the boundary between the two subarrays
|
||||
return i // Return the index of the pivot
|
||||
}
|
||||
|
||||
/* Quick sort */
|
||||
fun quickSort(nums: IntArray, left: Int, right: Int) {
|
||||
// Terminate recursion when subarray length is 1
|
||||
if (left >= right) return
|
||||
// Sentinel partition
|
||||
val pivot = partition(nums, left, right)
|
||||
// Recursively process the left subarray and right subarray
|
||||
quickSort(nums, left, pivot - 1)
|
||||
quickSort(nums, pivot + 1, right)
|
||||
}
|
||||
|
||||
/* Select the median of three candidate elements */
|
||||
fun medianThree(nums: IntArray, left: Int, mid: Int, right: Int): Int {
|
||||
val l = nums[left]
|
||||
val m = nums[mid]
|
||||
val r = nums[right]
|
||||
if ((m in l..r) || (m in r..l))
|
||||
return mid // m is between l and r
|
||||
if ((l in m..r) || (l in r..m))
|
||||
return left // l is between m and r
|
||||
return right
|
||||
}
|
||||
|
||||
/* Sentinel partition (median of three) */
|
||||
fun partitionMedian(nums: IntArray, left: Int, right: Int): Int {
|
||||
// Select the median of three candidate elements
|
||||
val med = medianThree(nums, left, (left + right) / 2, right)
|
||||
// Swap the median to the array's leftmost position
|
||||
swap(nums, left, med)
|
||||
// Use nums[left] as the pivot
|
||||
var i = left
|
||||
var j = right
|
||||
while (i < j) {
|
||||
while (i < j && nums[j] >= nums[left])
|
||||
j-- // Search from right to left for the first element smaller than the pivot
|
||||
while (i < j && nums[i] <= nums[left])
|
||||
i++ // Search from left to right for the first element greater than the pivot
|
||||
swap(nums, i, j) // Swap these two elements
|
||||
}
|
||||
swap(nums, i, left) // Swap the pivot to the boundary between the two subarrays
|
||||
return i // Return the index of the pivot
|
||||
}
|
||||
|
||||
/* Quick sort */
|
||||
fun quickSortMedian(nums: IntArray, left: Int, right: Int) {
|
||||
// Terminate recursion when subarray length is 1
|
||||
if (left >= right) return
|
||||
// Sentinel partition
|
||||
val pivot = partitionMedian(nums, left, right)
|
||||
// Recursively process the left subarray and right subarray
|
||||
quickSort(nums, left, pivot - 1)
|
||||
quickSort(nums, pivot + 1, right)
|
||||
}
|
||||
|
||||
/* Quick sort (recursion depth optimization) */
|
||||
fun quickSortTailCall(nums: IntArray, left: Int, right: Int) {
|
||||
// Terminate when subarray length is 1
|
||||
var l = left
|
||||
var r = right
|
||||
while (l < r) {
|
||||
// Sentinel partition operation
|
||||
val pivot = partition(nums, l, r)
|
||||
// Perform quick sort on the shorter of the two subarrays
|
||||
if (pivot - l < r - pivot) {
|
||||
quickSort(nums, l, pivot - 1) // Recursively sort the left subarray
|
||||
l = pivot + 1 // Remaining unsorted interval is [pivot + 1, right]
|
||||
} else {
|
||||
quickSort(nums, pivot + 1, r) // Recursively sort the right subarray
|
||||
r = pivot - 1 // Remaining unsorted interval is [left, pivot - 1]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Driver Code */
|
||||
fun main() {
|
||||
/* Quick sort */
|
||||
val nums = intArrayOf(2, 4, 1, 0, 3, 5)
|
||||
quickSort(nums, 0, nums.size - 1)
|
||||
println("After quick sort, nums = ${nums.contentToString()}")
|
||||
|
||||
/* Quick sort (recursion depth optimization) */
|
||||
val nums1 = intArrayOf(2, 4, 1, 0, 3, 5)
|
||||
quickSortMedian(nums1, 0, nums1.size - 1)
|
||||
println("After quick sort (median pivot optimization), nums1 = ${nums1.contentToString()}")
|
||||
|
||||
/* Quick sort (recursion depth optimization) */
|
||||
val nums2 = intArrayOf(2, 4, 1, 0, 3, 5)
|
||||
quickSortTailCall(nums2, 0, nums2.size - 1)
|
||||
println("After quick sort (recursion depth optimization), nums2 = ${nums2.contentToString()}")
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
/**
|
||||
* File: radix_sort.kt
|
||||
* Created Time: 2024-01-25
|
||||
* Author: curtishd (1023632660@qq.com)
|
||||
*/
|
||||
|
||||
package chapter_sorting
|
||||
|
||||
/* Get the k-th digit of element num, where exp = 10^(k-1) */
|
||||
fun digit(num: Int, exp: Int): Int {
|
||||
// Passing exp instead of k can avoid repeated expensive exponentiation here
|
||||
return (num / exp) % 10
|
||||
}
|
||||
|
||||
/* Counting sort (based on nums k-th digit) */
|
||||
fun countingSortDigit(nums: IntArray, exp: Int) {
|
||||
// Decimal digit range is 0~9, therefore need a bucket array of length 10
|
||||
val counter = IntArray(10)
|
||||
val n = nums.size
|
||||
// Count the occurrence of digits 0~9
|
||||
for (i in 0..<n) {
|
||||
val d = digit(nums[i], exp) // Get the k-th digit of nums[i], noted as d
|
||||
counter[d]++ // Count the occurrence of digit d
|
||||
}
|
||||
// Calculate prefix sum, converting "occurrence count" into "array index"
|
||||
for (i in 1..9) {
|
||||
counter[i] += counter[i - 1]
|
||||
}
|
||||
// Traverse in reverse, based on bucket statistics, place each element into res
|
||||
val res = IntArray(n)
|
||||
for (i in n - 1 downTo 0) {
|
||||
val d = digit(nums[i], exp)
|
||||
val j = counter[d] - 1 // Get the index j for d in the array
|
||||
res[j] = nums[i] // Place the current element at index j
|
||||
counter[d]-- // Decrease the count of d by 1
|
||||
}
|
||||
// Use result to overwrite the original array nums
|
||||
for (i in 0..<n)
|
||||
nums[i] = res[i]
|
||||
}
|
||||
|
||||
/* Radix sort */
|
||||
fun radixSort(nums: IntArray) {
|
||||
// Get the maximum element of the array, used to determine the maximum number of digits
|
||||
var m = Int.MIN_VALUE
|
||||
for (num in nums) if (num > m) m = num
|
||||
var exp = 1
|
||||
// Traverse from the lowest to the highest digit
|
||||
while (exp <= m) {
|
||||
// Perform counting sort on the k-th digit of array elements
|
||||
// k = 1 -> exp = 1
|
||||
// k = 2 -> exp = 10
|
||||
// i.e., exp = 10^(k-1)
|
||||
countingSortDigit(nums, exp)
|
||||
exp *= 10
|
||||
}
|
||||
}
|
||||
|
||||
/* Driver Code */
|
||||
fun main() {
|
||||
// Radix sort
|
||||
val nums = intArrayOf(
|
||||
10546151, 35663510, 42865989, 34862445, 81883077,
|
||||
88906420, 72429244, 30524779, 82060337, 63832996
|
||||
)
|
||||
radixSort(nums)
|
||||
println("After radix sort, nums = ${nums.contentToString()}")
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
/**
|
||||
* File: selection_sort.kt
|
||||
* Created Time: 2024-01-25
|
||||
* Author: curtishd (1023632660@qq.com)
|
||||
*/
|
||||
|
||||
package chapter_sorting
|
||||
|
||||
/* Selection sort */
|
||||
fun selectionSort(nums: IntArray) {
|
||||
val n = nums.size
|
||||
// Outer loop: unsorted interval is [i, n-1]
|
||||
for (i in 0..<n - 1) {
|
||||
var k = i
|
||||
// Inner loop: find the smallest element within the unsorted interval
|
||||
for (j in i + 1..<n) {
|
||||
if (nums[j] < nums[k])
|
||||
k = j // Record the index of the smallest element
|
||||
}
|
||||
// Swap the smallest element with the first element of the unsorted interval
|
||||
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("After selection sort, 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
|
||||
|
||||
/* Double-ended queue based on circular array implementation */
|
||||
/* Constructor */
|
||||
class ArrayDeque(capacity: Int) {
|
||||
private var nums: IntArray = IntArray(capacity) // Array for storing double-ended queue elements
|
||||
private var front: Int = 0 // Front pointer, points to the front of the queue element
|
||||
private var queSize: Int = 0 // Double-ended queue length
|
||||
|
||||
/* Get the capacity of the double-ended queue */
|
||||
fun capacity(): Int {
|
||||
return nums.size
|
||||
}
|
||||
|
||||
/* Get the length of the double-ended queue */
|
||||
fun size(): Int {
|
||||
return queSize
|
||||
}
|
||||
|
||||
/* Check if the double-ended queue is empty */
|
||||
fun isEmpty(): Boolean {
|
||||
return queSize == 0
|
||||
}
|
||||
|
||||
/* Calculate circular array index */
|
||||
private fun index(i: Int): Int {
|
||||
// Use modulo operation to wrap the array head and tail together
|
||||
// When i passes the tail of the array, return to the head
|
||||
// When i passes the head of the array, return to the tail
|
||||
return (i + capacity()) % capacity()
|
||||
}
|
||||
|
||||
/* Front of the queue enqueue */
|
||||
fun pushFirst(num: Int) {
|
||||
if (queSize == capacity()) {
|
||||
println("Double-ended queue is full")
|
||||
return
|
||||
}
|
||||
// Use modulo operation to wrap front around to the tail after passing the head of the array
|
||||
// Add num to the front of the queue
|
||||
front = index(front - 1)
|
||||
// Add num to front of queue
|
||||
nums[front] = num
|
||||
queSize++
|
||||
}
|
||||
|
||||
/* Rear of the queue enqueue */
|
||||
fun pushLast(num: Int) {
|
||||
if (queSize == capacity()) {
|
||||
println("Double-ended queue is full")
|
||||
return
|
||||
}
|
||||
// Use modulo operation to wrap rear around to the head after passing the tail of the array
|
||||
val rear = index(front + queSize)
|
||||
// Front pointer moves one position backward
|
||||
nums[rear] = num
|
||||
queSize++
|
||||
}
|
||||
|
||||
/* Rear of the queue dequeue */
|
||||
fun popFirst(): Int {
|
||||
val num = peekFirst()
|
||||
// Move front pointer backward by one position
|
||||
front = index(front + 1)
|
||||
queSize--
|
||||
return num
|
||||
}
|
||||
|
||||
/* Access rear of the queue element */
|
||||
fun popLast(): Int {
|
||||
val num = peekLast()
|
||||
queSize--
|
||||
return num
|
||||
}
|
||||
|
||||
/* Return list for printing */
|
||||
fun peekFirst(): Int {
|
||||
if (isEmpty()) throw IndexOutOfBoundsException()
|
||||
return nums[front]
|
||||
}
|
||||
|
||||
/* Driver Code */
|
||||
fun peekLast(): Int {
|
||||
if (isEmpty()) throw IndexOutOfBoundsException()
|
||||
// Initialize double-ended queue
|
||||
val last = index(front + queSize - 1)
|
||||
return nums[last]
|
||||
}
|
||||
|
||||
/* Return array for printing */
|
||||
fun toArray(): IntArray {
|
||||
// Elements enqueue
|
||||
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() {
|
||||
/* Get the length of the double-ended queue */
|
||||
val deque = ArrayDeque(10)
|
||||
deque.pushLast(3)
|
||||
deque.pushLast(2)
|
||||
deque.pushLast(5)
|
||||
println("Deque deque = ${deque.toArray().contentToString()}")
|
||||
|
||||
/* Update element */
|
||||
val peekFirst = deque.peekFirst()
|
||||
println("Front element peekFirst = $peekFirst")
|
||||
val peekLast = deque.peekLast()
|
||||
println("Rear element peekLast = $peekLast")
|
||||
|
||||
/* Elements enqueue */
|
||||
deque.pushLast(4)
|
||||
println("After element 4 enqueues at rear, deque = ${deque.toArray().contentToString()}")
|
||||
deque.pushFirst(1)
|
||||
println("After element 1 enqueues at front, deque = ${deque.toArray().contentToString()}")
|
||||
|
||||
/* Element dequeue */
|
||||
val popLast = deque.popLast()
|
||||
println("Dequeue rear element = ${popLast}, after rear dequeue deque = ${deque.toArray().contentToString()}")
|
||||
val popFirst = deque.popFirst()
|
||||
println("Dequeue front element = ${popFirst}, after front dequeue deque = ${deque.toArray().contentToString()}")
|
||||
|
||||
/* Get the length of the double-ended queue */
|
||||
val size = deque.size()
|
||||
println("Deque length size = $size")
|
||||
|
||||
/* Check if the double-ended queue is empty */
|
||||
val isEmpty = deque.isEmpty()
|
||||
println("Is deque empty = $isEmpty")
|
||||
}
|
||||
@@ -0,0 +1,110 @@
|
||||
/**
|
||||
* File: array_queue.kt
|
||||
* Created Time: 2024-01-25
|
||||
* Author: curtishd (1023632660@qq.com)
|
||||
*/
|
||||
|
||||
package chapter_stack_and_queue
|
||||
|
||||
/* Queue based on circular array implementation */
|
||||
class ArrayQueue(capacity: Int) {
|
||||
private val nums: IntArray = IntArray(capacity) // Array for storing queue elements
|
||||
private var front: Int = 0 // Front pointer, points to the front of the queue element
|
||||
private var queSize: Int = 0 // Queue length
|
||||
|
||||
/* Get the capacity of the queue */
|
||||
fun capacity(): Int {
|
||||
return nums.size
|
||||
}
|
||||
|
||||
/* Get the length of the queue */
|
||||
fun size(): Int {
|
||||
return queSize
|
||||
}
|
||||
|
||||
/* Check if the queue is empty */
|
||||
fun isEmpty(): Boolean {
|
||||
return queSize == 0
|
||||
}
|
||||
|
||||
/* Enqueue */
|
||||
fun push(num: Int) {
|
||||
if (queSize == capacity()) {
|
||||
println("Queue is full")
|
||||
return
|
||||
}
|
||||
// Use modulo operation to wrap rear around to the head after passing the tail of the array
|
||||
// Add num to the rear of the queue
|
||||
val rear = (front + queSize) % capacity()
|
||||
// Front pointer moves one position backward
|
||||
nums[rear] = num
|
||||
queSize++
|
||||
}
|
||||
|
||||
/* Dequeue */
|
||||
fun pop(): Int {
|
||||
val num = peek()
|
||||
// Move front pointer backward by one position, if it passes the tail, return to array head
|
||||
front = (front + 1) % capacity()
|
||||
queSize--
|
||||
return num
|
||||
}
|
||||
|
||||
/* Return list for printing */
|
||||
fun peek(): Int {
|
||||
if (isEmpty()) throw IndexOutOfBoundsException()
|
||||
return nums[front]
|
||||
}
|
||||
|
||||
/* Return array */
|
||||
fun toArray(): IntArray {
|
||||
// Elements enqueue
|
||||
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() {
|
||||
/* Access front of the queue element */
|
||||
val capacity = 10
|
||||
val queue = ArrayQueue(capacity)
|
||||
|
||||
/* Elements enqueue */
|
||||
queue.push(1)
|
||||
queue.push(3)
|
||||
queue.push(2)
|
||||
queue.push(5)
|
||||
queue.push(4)
|
||||
println("Queue queue = ${queue.toArray().contentToString()}")
|
||||
|
||||
/* Return list for printing */
|
||||
val peek = queue.peek()
|
||||
println("Front element peek = $peek")
|
||||
|
||||
/* Element dequeue */
|
||||
val pop = queue.pop()
|
||||
println("Dequeue element pop = ${pop}, after dequeue queue = ${queue.toArray().contentToString()}")
|
||||
|
||||
/* Get the length of the queue */
|
||||
val size = queue.size()
|
||||
println("Queue length size = $size")
|
||||
|
||||
/* Check if the queue is empty */
|
||||
val isEmpty = queue.isEmpty()
|
||||
println("Is queue empty = $isEmpty")
|
||||
|
||||
/* Test circular array */
|
||||
for (i in 0..9) {
|
||||
queue.push(i)
|
||||
queue.pop()
|
||||
println("After round $i enqueue + dequeue, 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
|
||||
|
||||
/* Stack based on array implementation */
|
||||
class ArrayStack {
|
||||
// Initialize list (dynamic array)
|
||||
private val stack = mutableListOf<Int>()
|
||||
|
||||
/* Get the length of the stack */
|
||||
fun size(): Int {
|
||||
return stack.size
|
||||
}
|
||||
|
||||
/* Check if the stack is empty */
|
||||
fun isEmpty(): Boolean {
|
||||
return size() == 0
|
||||
}
|
||||
|
||||
/* Push */
|
||||
fun push(num: Int) {
|
||||
stack.add(num)
|
||||
}
|
||||
|
||||
/* Pop */
|
||||
fun pop(): Int {
|
||||
if (isEmpty()) throw IndexOutOfBoundsException()
|
||||
return stack.removeAt(size() - 1)
|
||||
}
|
||||
|
||||
/* Return list for printing */
|
||||
fun peek(): Int {
|
||||
if (isEmpty()) throw IndexOutOfBoundsException()
|
||||
return stack[size() - 1]
|
||||
}
|
||||
|
||||
/* Convert List to Array and return */
|
||||
fun toArray(): Array<Any> {
|
||||
return stack.toTypedArray()
|
||||
}
|
||||
}
|
||||
|
||||
/* Driver Code */
|
||||
fun main() {
|
||||
/* Access top of the stack element */
|
||||
val stack = ArrayStack()
|
||||
|
||||
/* Elements push onto stack */
|
||||
stack.push(1)
|
||||
stack.push(3)
|
||||
stack.push(2)
|
||||
stack.push(5)
|
||||
stack.push(4)
|
||||
println("Stack stack = ${stack.toArray().contentToString()}")
|
||||
|
||||
/* Return list for printing */
|
||||
val peek = stack.peek()
|
||||
println("Top element peek = $peek")
|
||||
|
||||
/* Element pop from stack */
|
||||
val pop = stack.pop()
|
||||
println("Pop element pop = $pop, after pop stack = ${stack.toArray().contentToString()}")
|
||||
|
||||
/* Get the length of the stack */
|
||||
val size = stack.size()
|
||||
println("Stack length size = $size")
|
||||
|
||||
/* Check if empty */
|
||||
val isEmpty = stack.isEmpty()
|
||||
println("Is stack empty = $isEmpty")
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
/**
|
||||
* File: deque.kt
|
||||
* Created Time: 2024-01-25
|
||||
* Author: curtishd (1023632660@qq.com)
|
||||
*/
|
||||
|
||||
package chapter_stack_and_queue
|
||||
|
||||
import java.util.*
|
||||
|
||||
/* Driver Code */
|
||||
fun main() {
|
||||
/* Get the length of the double-ended queue */
|
||||
val deque = LinkedList<Int>()
|
||||
deque.offerLast(3)
|
||||
deque.offerLast(2)
|
||||
deque.offerLast(5)
|
||||
println("Deque deque = $deque")
|
||||
|
||||
/* Update element */
|
||||
val peekFirst = deque.peekFirst()
|
||||
println("Front element peekFirst = $peekFirst")
|
||||
val peekLast = deque.peekLast()
|
||||
println("Rear element peekLast = $peekLast")
|
||||
|
||||
/* Elements enqueue */
|
||||
deque.offerLast(4)
|
||||
println("After element 4 enqueues at rear, deque = $deque")
|
||||
deque.offerFirst(1)
|
||||
println("After element 1 enqueues at front, deque = $deque")
|
||||
|
||||
/* Element dequeue */
|
||||
val popLast = deque.pollLast()
|
||||
println("Dequeue rear element = $popLast, after rear dequeue deque = $deque")
|
||||
val popFirst = deque.pollFirst()
|
||||
println("Dequeue front element = $popFirst, after front dequeue deque = $deque")
|
||||
|
||||
/* Get the length of the double-ended queue */
|
||||
val size = deque.size
|
||||
println("Deque length size = $size")
|
||||
|
||||
/* Check if the double-ended queue is empty */
|
||||
val isEmpty = deque.isEmpty()
|
||||
println("Is deque empty = $isEmpty")
|
||||
}
|
||||
@@ -0,0 +1,163 @@
|
||||
/**
|
||||
* File: linkedlist_deque.kt
|
||||
* Created Time: 2024-01-25
|
||||
* Author: curtishd (1023632660@qq.com)
|
||||
*/
|
||||
|
||||
package chapter_stack_and_queue
|
||||
|
||||
/* Doubly linked list node */
|
||||
class ListNode(var _val: Int) {
|
||||
// Node value
|
||||
var next: ListNode? = null // Successor node reference
|
||||
var prev: ListNode? = null // Predecessor node reference
|
||||
}
|
||||
|
||||
/* Double-ended queue based on doubly linked list implementation */
|
||||
class LinkedListDeque {
|
||||
private var front: ListNode? = null // Head node front
|
||||
private var rear: ListNode? = null // Tail node rear
|
||||
private var queSize: Int = 0 // Length of the double-ended queue
|
||||
|
||||
/* Get the length of the double-ended queue */
|
||||
fun size(): Int {
|
||||
return queSize
|
||||
}
|
||||
|
||||
/* Check if the double-ended queue is empty */
|
||||
fun isEmpty(): Boolean {
|
||||
return size() == 0
|
||||
}
|
||||
|
||||
/* Enqueue operation */
|
||||
fun push(num: Int, isFront: Boolean) {
|
||||
val node = ListNode(num)
|
||||
// If the linked list is empty, make both front and rear point to node
|
||||
if (isEmpty()) {
|
||||
rear = node
|
||||
front = rear
|
||||
// Front of the queue enqueue operation
|
||||
} else if (isFront) {
|
||||
// Add node to the head of the linked list
|
||||
front?.prev = node
|
||||
node.next = front
|
||||
front = node // Update head node
|
||||
// Rear of the queue enqueue operation
|
||||
} else {
|
||||
// Add node to the tail of the linked list
|
||||
rear?.next = node
|
||||
node.prev = rear
|
||||
rear = node // Update tail node
|
||||
}
|
||||
queSize++ // Update queue length
|
||||
}
|
||||
|
||||
/* Front of the queue enqueue */
|
||||
fun pushFirst(num: Int) {
|
||||
push(num, true)
|
||||
}
|
||||
|
||||
/* Rear of the queue enqueue */
|
||||
fun pushLast(num: Int) {
|
||||
push(num, false)
|
||||
}
|
||||
|
||||
/* Dequeue operation */
|
||||
fun pop(isFront: Boolean): Int {
|
||||
if (isEmpty())
|
||||
throw IndexOutOfBoundsException()
|
||||
val _val: Int
|
||||
// Temporarily store head node value
|
||||
if (isFront) {
|
||||
_val = front!!._val // Delete head node
|
||||
// Delete head node
|
||||
val fNext = front!!.next
|
||||
if (fNext != null) {
|
||||
fNext.prev = null
|
||||
front!!.next = null
|
||||
}
|
||||
front = fNext // Update head node
|
||||
// Temporarily store tail node value
|
||||
} else {
|
||||
_val = rear!!._val // Delete tail node
|
||||
// Update tail node
|
||||
val rPrev = rear!!.prev
|
||||
if (rPrev != null) {
|
||||
rPrev.next = null
|
||||
rear!!.prev = null
|
||||
}
|
||||
rear = rPrev // Update tail node
|
||||
}
|
||||
queSize-- // Update queue length
|
||||
return _val
|
||||
}
|
||||
|
||||
/* Rear of the queue dequeue */
|
||||
fun popFirst(): Int {
|
||||
return pop(true)
|
||||
}
|
||||
|
||||
/* Access rear of the queue element */
|
||||
fun popLast(): Int {
|
||||
return pop(false)
|
||||
}
|
||||
|
||||
/* Return list for printing */
|
||||
fun peekFirst(): Int {
|
||||
if (isEmpty()) throw IndexOutOfBoundsException()
|
||||
return front!!._val
|
||||
}
|
||||
|
||||
/* Driver Code */
|
||||
fun peekLast(): Int {
|
||||
if (isEmpty()) throw IndexOutOfBoundsException()
|
||||
return rear!!._val
|
||||
}
|
||||
|
||||
/* Return array for printing */
|
||||
fun toArray(): IntArray {
|
||||
var node = front
|
||||
val res = IntArray(size())
|
||||
for (i in res.indices) {
|
||||
res[i] = node!!._val
|
||||
node = node.next
|
||||
}
|
||||
return res
|
||||
}
|
||||
}
|
||||
|
||||
/* Driver Code */
|
||||
fun main() {
|
||||
/* Get the length of the double-ended queue */
|
||||
val deque = LinkedListDeque()
|
||||
deque.pushLast(3)
|
||||
deque.pushLast(2)
|
||||
deque.pushLast(5)
|
||||
println("Deque deque = ${deque.toArray().contentToString()}")
|
||||
|
||||
/* Update element */
|
||||
val peekFirst = deque.peekFirst()
|
||||
println("Front element peekFirst = $peekFirst")
|
||||
val peekLast = deque.peekLast()
|
||||
println("Rear element peekLast = $peekLast")
|
||||
|
||||
/* Elements enqueue */
|
||||
deque.pushLast(4)
|
||||
println("After element 4 enqueues at rear, deque = ${deque.toArray().contentToString()}")
|
||||
deque.pushFirst(1)
|
||||
println("After element 1 enqueues at front, deque = ${deque.toArray().contentToString()}")
|
||||
|
||||
/* Element dequeue */
|
||||
val popLast = deque.popLast()
|
||||
println("Dequeue rear element = ${popLast}, after rear dequeue deque = ${deque.toArray().contentToString()}")
|
||||
val popFirst = deque.popFirst()
|
||||
println("Dequeue front element = ${popFirst}, after front dequeue deque = ${deque.toArray().contentToString()}")
|
||||
|
||||
/* Get the length of the double-ended queue */
|
||||
val size = deque.size()
|
||||
println("Deque length size = $size")
|
||||
|
||||
/* Check if the double-ended queue is empty */
|
||||
val isEmpty = deque.isEmpty()
|
||||
println("Is deque empty = $isEmpty")
|
||||
}
|
||||
@@ -0,0 +1,98 @@
|
||||
/**
|
||||
* File: linkedlist_queue.kt
|
||||
* Created Time: 2024-01-25
|
||||
* Author: curtishd (1023632660@qq.com)
|
||||
*/
|
||||
|
||||
package chapter_stack_and_queue
|
||||
|
||||
/* Queue based on linked list implementation */
|
||||
class LinkedListQueue(
|
||||
// Head node front, tail node rear
|
||||
private var front: ListNode? = null,
|
||||
private var rear: ListNode? = null,
|
||||
private var queSize: Int = 0
|
||||
) {
|
||||
|
||||
/* Get the length of the queue */
|
||||
fun size(): Int {
|
||||
return queSize
|
||||
}
|
||||
|
||||
/* Check if the queue is empty */
|
||||
fun isEmpty(): Boolean {
|
||||
return size() == 0
|
||||
}
|
||||
|
||||
/* Enqueue */
|
||||
fun push(num: Int) {
|
||||
// Add num after the tail node
|
||||
val node = ListNode(num)
|
||||
// If the queue is empty, make both front and rear point to the node
|
||||
if (front == null) {
|
||||
front = node
|
||||
rear = node
|
||||
// If the queue is not empty, add the node after the tail node
|
||||
} else {
|
||||
rear?.next = node
|
||||
rear = node
|
||||
}
|
||||
queSize++
|
||||
}
|
||||
|
||||
/* Dequeue */
|
||||
fun pop(): Int {
|
||||
val num = peek()
|
||||
// Delete head node
|
||||
front = front?.next
|
||||
queSize--
|
||||
return num
|
||||
}
|
||||
|
||||
/* Return list for printing */
|
||||
fun peek(): Int {
|
||||
if (isEmpty()) throw IndexOutOfBoundsException()
|
||||
return front!!._val
|
||||
}
|
||||
|
||||
/* Convert linked list to Array and return */
|
||||
fun toArray(): IntArray {
|
||||
var node = front
|
||||
val res = IntArray(size())
|
||||
for (i in res.indices) {
|
||||
res[i] = node!!._val
|
||||
node = node.next
|
||||
}
|
||||
return res
|
||||
}
|
||||
}
|
||||
|
||||
/* Driver Code */
|
||||
fun main() {
|
||||
/* Access front of the queue element */
|
||||
val queue = LinkedListQueue()
|
||||
|
||||
/* Elements enqueue */
|
||||
queue.push(1)
|
||||
queue.push(3)
|
||||
queue.push(2)
|
||||
queue.push(5)
|
||||
queue.push(4)
|
||||
println("Queue queue = ${queue.toArray().contentToString()}")
|
||||
|
||||
/* Return list for printing */
|
||||
val peek = queue.peek()
|
||||
println("Front element peek = $peek")
|
||||
|
||||
/* Element dequeue */
|
||||
val pop = queue.pop()
|
||||
println("Dequeue element pop = $pop, after dequeue queue = ${queue.toArray().contentToString()}")
|
||||
|
||||
/* Get the length of the queue */
|
||||
val size = queue.size()
|
||||
println("Queue length size = $size")
|
||||
|
||||
/* Check if the queue is empty */
|
||||
val isEmpty = queue.isEmpty()
|
||||
println("Is queue empty = $isEmpty")
|
||||
}
|
||||
@@ -0,0 +1,87 @@
|
||||
/**
|
||||
* File: linkedlist_stack.kt
|
||||
* Created Time: 2024-01-25
|
||||
* Author: curtishd (1023632660@qq.com)
|
||||
*/
|
||||
|
||||
package chapter_stack_and_queue
|
||||
|
||||
/* Stack based on linked list implementation */
|
||||
class LinkedListStack(
|
||||
private var stackPeek: ListNode? = null, // Use head node as stack top
|
||||
private var stkSize: Int = 0 // Stack length
|
||||
) {
|
||||
|
||||
/* Get the length of the stack */
|
||||
fun size(): Int {
|
||||
return stkSize
|
||||
}
|
||||
|
||||
/* Check if the stack is empty */
|
||||
fun isEmpty(): Boolean {
|
||||
return size() == 0
|
||||
}
|
||||
|
||||
/* Push */
|
||||
fun push(num: Int) {
|
||||
val node = ListNode(num)
|
||||
node.next = stackPeek
|
||||
stackPeek = node
|
||||
stkSize++
|
||||
}
|
||||
|
||||
/* Pop */
|
||||
fun pop(): Int? {
|
||||
val num = peek()
|
||||
stackPeek = stackPeek?.next
|
||||
stkSize--
|
||||
return num
|
||||
}
|
||||
|
||||
/* Return list for printing */
|
||||
fun peek(): Int? {
|
||||
if (isEmpty()) throw IndexOutOfBoundsException()
|
||||
return stackPeek?._val
|
||||
}
|
||||
|
||||
/* Convert List to Array and return */
|
||||
fun toArray(): IntArray {
|
||||
var node = stackPeek
|
||||
val res = IntArray(size())
|
||||
for (i in res.size - 1 downTo 0) {
|
||||
res[i] = node?._val!!
|
||||
node = node.next
|
||||
}
|
||||
return res
|
||||
}
|
||||
}
|
||||
|
||||
/* Driver Code */
|
||||
fun main() {
|
||||
/* Access top of the stack element */
|
||||
val stack = LinkedListStack()
|
||||
|
||||
/* Elements push onto stack */
|
||||
stack.push(1)
|
||||
stack.push(3)
|
||||
stack.push(2)
|
||||
stack.push(5)
|
||||
stack.push(4)
|
||||
println("Stack stack = ${stack.toArray().contentToString()}")
|
||||
|
||||
/* Return list for printing */
|
||||
val peek = stack.peek()!!
|
||||
println("Top element peek = $peek")
|
||||
|
||||
/* Element pop from stack */
|
||||
val pop = stack.pop()!!
|
||||
println("Pop element pop = $pop, after pop stack = ${stack.toArray().contentToString()}")
|
||||
|
||||
/* Get the length of the stack */
|
||||
val size = stack.size()
|
||||
println("Stack length size = $size")
|
||||
|
||||
/* Check if empty */
|
||||
val isEmpty = stack.isEmpty()
|
||||
println("Is stack empty = $isEmpty")
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
/**
|
||||
* File: queue.kt
|
||||
* Created Time: 2024-01-25
|
||||
* Author: curtishd (1023632660@qq.com)
|
||||
*/
|
||||
|
||||
package chapter_stack_and_queue
|
||||
|
||||
import java.util.*
|
||||
|
||||
/* Driver Code */
|
||||
fun main() {
|
||||
/* Access front of the queue element */
|
||||
val queue = LinkedList<Int>()
|
||||
|
||||
/* Elements enqueue */
|
||||
queue.offer(1)
|
||||
queue.offer(3)
|
||||
queue.offer(2)
|
||||
queue.offer(5)
|
||||
queue.offer(4)
|
||||
println("Queue queue = $queue")
|
||||
|
||||
/* Return list for printing */
|
||||
val peek = queue.peek()
|
||||
println("Front element peek = $peek")
|
||||
|
||||
/* Element dequeue */
|
||||
val pop = queue.poll()
|
||||
println("Dequeue element pop = $pop, after dequeue queue = $queue")
|
||||
|
||||
/* Get the length of the queue */
|
||||
val size = queue.size
|
||||
println("Queue length size = $size")
|
||||
|
||||
/* Check if the queue is empty */
|
||||
val isEmpty = queue.isEmpty()
|
||||
println("Is queue empty = $isEmpty")
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
/**
|
||||
* File: stack.kt
|
||||
* Created Time: 2024-01-25
|
||||
* Author: curtishd (1023632660@qq.com)
|
||||
*/
|
||||
|
||||
package chapter_stack_and_queue
|
||||
|
||||
import java.util.*
|
||||
|
||||
/* Driver Code */
|
||||
fun main() {
|
||||
/* Access top of the stack element */
|
||||
val stack = Stack<Int>()
|
||||
|
||||
/* Elements push onto stack */
|
||||
stack.push(1)
|
||||
stack.push(3)
|
||||
stack.push(2)
|
||||
stack.push(5)
|
||||
stack.push(4)
|
||||
println("Stack stack = $stack")
|
||||
|
||||
/* Return list for printing */
|
||||
val peek = stack.peek()
|
||||
println("Top element peek = $peek")
|
||||
|
||||
/* Element pop from stack */
|
||||
val pop = stack.pop()
|
||||
println("Pop element pop = $pop, after pop stack = $stack")
|
||||
|
||||
/* Get the length of the stack */
|
||||
val size = stack.size
|
||||
println("Stack length size = $size")
|
||||
|
||||
/* Check if empty */
|
||||
val isEmpty = stack.isEmpty()
|
||||
println("Is stack empty = $isEmpty")
|
||||
}
|
||||
@@ -0,0 +1,127 @@
|
||||
/**
|
||||
* File: array_binary_tree.kt
|
||||
* Created Time: 2024-01-25
|
||||
* Author: curtishd (1023632660@qq.com)
|
||||
*/
|
||||
|
||||
package chapter_tree
|
||||
|
||||
import utils.TreeNode
|
||||
import utils.printTree
|
||||
|
||||
/* Binary tree class represented by array */
|
||||
class ArrayBinaryTree(private val tree: MutableList<Int?>) {
|
||||
/* List capacity */
|
||||
fun size(): Int {
|
||||
return tree.size
|
||||
}
|
||||
|
||||
/* Get value of node at index i */
|
||||
fun _val(i: Int): Int? {
|
||||
// If index out of bounds, return null to represent empty position
|
||||
if (i < 0 || i >= size()) return null
|
||||
return tree[i]
|
||||
}
|
||||
|
||||
/* Get index of left child node of node at index i */
|
||||
fun left(i: Int): Int {
|
||||
return 2 * i + 1
|
||||
}
|
||||
|
||||
/* Get index of right child node of node at index i */
|
||||
fun right(i: Int): Int {
|
||||
return 2 * i + 2
|
||||
}
|
||||
|
||||
/* Get index of parent node of node at index i */
|
||||
fun parent(i: Int): Int {
|
||||
return (i - 1) / 2
|
||||
}
|
||||
|
||||
/* Level-order traversal */
|
||||
fun levelOrder(): MutableList<Int?> {
|
||||
val res = mutableListOf<Int?>()
|
||||
// Traverse array directly
|
||||
for (i in 0..<size()) {
|
||||
if (_val(i) != null)
|
||||
res.add(_val(i))
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
/* Depth-first traversal */
|
||||
fun dfs(i: Int, order: String, res: MutableList<Int?>) {
|
||||
// If empty position, return
|
||||
if (_val(i) == null)
|
||||
return
|
||||
// Preorder traversal
|
||||
if ("pre" == order)
|
||||
res.add(_val(i))
|
||||
dfs(left(i), order, res)
|
||||
// Inorder traversal
|
||||
if ("in" == order)
|
||||
res.add(_val(i))
|
||||
dfs(right(i), order, res)
|
||||
// Postorder traversal
|
||||
if ("post" == order)
|
||||
res.add(_val(i))
|
||||
}
|
||||
|
||||
/* Preorder traversal */
|
||||
fun preOrder(): MutableList<Int?> {
|
||||
val res = mutableListOf<Int?>()
|
||||
dfs(0, "pre", res)
|
||||
return res
|
||||
}
|
||||
|
||||
/* Inorder traversal */
|
||||
fun inOrder(): MutableList<Int?> {
|
||||
val res = mutableListOf<Int?>()
|
||||
dfs(0, "in", res)
|
||||
return res
|
||||
}
|
||||
|
||||
/* Postorder traversal */
|
||||
fun postOrder(): MutableList<Int?> {
|
||||
val res = mutableListOf<Int?>()
|
||||
dfs(0, "post", res)
|
||||
return res
|
||||
}
|
||||
}
|
||||
|
||||
/* Driver Code */
|
||||
fun main() {
|
||||
// Initialize binary tree
|
||||
// Here we use a function to generate binary tree directly from list
|
||||
val arr = mutableListOf(1, 2, 3, 4, null, 6, 7, 8, 9, null, null, 12, null, null, 15)
|
||||
|
||||
val root = TreeNode.listToTree(arr)
|
||||
println("\nInitialize binary tree\n")
|
||||
println("Array representation of binary tree:")
|
||||
println(arr)
|
||||
println("Linked list representation of binary tree:")
|
||||
printTree(root)
|
||||
|
||||
// Binary tree class represented by array
|
||||
val abt = ArrayBinaryTree(arr)
|
||||
|
||||
// Access node
|
||||
val i = 1
|
||||
val l = abt.left(i)
|
||||
val r = abt.right(i)
|
||||
val p = abt.parent(i)
|
||||
println("Current node index is $i, value is ${abt._val(i)}")
|
||||
println("Its left child index is $l, value is ${abt._val(l)}")
|
||||
println("Its right child index is $r, value is ${abt._val(r)}")
|
||||
println("Its parent node index is $p, value is ${abt._val(p)}")
|
||||
|
||||
// Traverse tree
|
||||
var res = abt.levelOrder()
|
||||
println("\nLevel-order traversal is: $res")
|
||||
res = abt.preOrder()
|
||||
println("Pre-order traversal is: $res")
|
||||
res = abt.inOrder()
|
||||
println("In-order traversal is: $res")
|
||||
res = abt.postOrder()
|
||||
println("Post-order traversal is: $res")
|
||||
}
|
||||
@@ -0,0 +1,223 @@
|
||||
/**
|
||||
* File: avl_tree.kt
|
||||
* Created Time: 2024-01-25
|
||||
* Author: curtishd (1023632660@qq.com)
|
||||
*/
|
||||
|
||||
package chapter_tree
|
||||
|
||||
import utils.TreeNode
|
||||
import utils.printTree
|
||||
import kotlin.math.max
|
||||
|
||||
/* AVL tree */
|
||||
class AVLTree {
|
||||
var root: TreeNode? = null // Root node
|
||||
|
||||
/* Get node height */
|
||||
fun height(node: TreeNode?): Int {
|
||||
// Empty node height is -1, leaf node height is 0
|
||||
return node?.height ?: -1
|
||||
}
|
||||
|
||||
/* Update node height */
|
||||
private fun updateHeight(node: TreeNode?) {
|
||||
// Node height equals the height of the tallest subtree + 1
|
||||
node?.height = max(height(node?.left), height(node?.right)) + 1
|
||||
}
|
||||
|
||||
/* Get balance factor */
|
||||
fun balanceFactor(node: TreeNode?): Int {
|
||||
// Empty node balance factor is 0
|
||||
if (node == null) return 0
|
||||
// Node balance factor = left subtree height - right subtree height
|
||||
return height(node.left) - height(node.right)
|
||||
}
|
||||
|
||||
/* Right rotation operation */
|
||||
private fun rightRotate(node: TreeNode?): TreeNode {
|
||||
val child = node!!.left
|
||||
val grandChild = child!!.right
|
||||
// Using child as pivot, rotate node to the right
|
||||
child.right = node
|
||||
node.left = grandChild
|
||||
// Update node height
|
||||
updateHeight(node)
|
||||
updateHeight(child)
|
||||
// Return root node of subtree after rotation
|
||||
return child
|
||||
}
|
||||
|
||||
/* Left rotation operation */
|
||||
private fun leftRotate(node: TreeNode?): TreeNode {
|
||||
val child = node!!.right
|
||||
val grandChild = child!!.left
|
||||
// Using child as pivot, rotate node to the left
|
||||
child.left = node
|
||||
node.right = grandChild
|
||||
// Update node height
|
||||
updateHeight(node)
|
||||
updateHeight(child)
|
||||
// Return root node of subtree after rotation
|
||||
return child
|
||||
}
|
||||
|
||||
/* Perform rotation operation to restore balance to this subtree */
|
||||
private fun rotate(node: TreeNode): TreeNode {
|
||||
// Get balance factor of node
|
||||
val balanceFactor = balanceFactor(node)
|
||||
// Left-leaning tree
|
||||
if (balanceFactor > 1) {
|
||||
if (balanceFactor(node.left) >= 0) {
|
||||
// Right rotation
|
||||
return rightRotate(node)
|
||||
} else {
|
||||
// First left rotation then right rotation
|
||||
node.left = leftRotate(node.left)
|
||||
return rightRotate(node)
|
||||
}
|
||||
}
|
||||
// Right-leaning tree
|
||||
if (balanceFactor < -1) {
|
||||
if (balanceFactor(node.right) <= 0) {
|
||||
// Left rotation
|
||||
return leftRotate(node)
|
||||
} else {
|
||||
// First right rotation then left rotation
|
||||
node.right = rightRotate(node.right)
|
||||
return leftRotate(node)
|
||||
}
|
||||
}
|
||||
// Balanced tree, no rotation needed, return directly
|
||||
return node
|
||||
}
|
||||
|
||||
/* Insert node */
|
||||
fun insert(_val: Int) {
|
||||
root = insertHelper(root, _val)
|
||||
}
|
||||
|
||||
/* Recursively insert node (helper method) */
|
||||
private fun insertHelper(n: TreeNode?, _val: Int): TreeNode {
|
||||
if (n == null)
|
||||
return TreeNode(_val)
|
||||
var node = n
|
||||
/* 1. Find insertion position and insert node */
|
||||
if (_val < node._val)
|
||||
node.left = insertHelper(node.left, _val)
|
||||
else if (_val > node._val)
|
||||
node.right = insertHelper(node.right, _val)
|
||||
else
|
||||
return node // Duplicate node not inserted, return directly
|
||||
updateHeight(node) // Update node height
|
||||
/* 2. Perform rotation operation to restore balance to this subtree */
|
||||
node = rotate(node)
|
||||
// Return root node of subtree
|
||||
return node
|
||||
}
|
||||
|
||||
/* Remove node */
|
||||
fun remove(_val: Int) {
|
||||
root = removeHelper(root, _val)
|
||||
}
|
||||
|
||||
/* Recursively delete node (helper method) */
|
||||
private fun removeHelper(n: TreeNode?, _val: Int): TreeNode? {
|
||||
var node = n ?: return null
|
||||
/* 1. Find node and delete */
|
||||
if (_val < node._val)
|
||||
node.left = removeHelper(node.left, _val)
|
||||
else if (_val > node._val)
|
||||
node.right = removeHelper(node.right, _val)
|
||||
else {
|
||||
if (node.left == null || node.right == null) {
|
||||
val child = if (node.left != null)
|
||||
node.left
|
||||
else
|
||||
node.right
|
||||
// Number of child nodes = 0, delete node directly and return
|
||||
if (child == null)
|
||||
return null
|
||||
// Number of child nodes = 1, delete node directly
|
||||
else
|
||||
node = child
|
||||
} else {
|
||||
// Number of child nodes = 2, delete the next node in inorder traversal and replace current node with it
|
||||
var temp = node.right
|
||||
while (temp!!.left != null) {
|
||||
temp = temp.left
|
||||
}
|
||||
node.right = removeHelper(node.right, temp._val)
|
||||
node._val = temp._val
|
||||
}
|
||||
}
|
||||
updateHeight(node) // Update node height
|
||||
/* 2. Perform rotation operation to restore balance to this subtree */
|
||||
node = rotate(node)
|
||||
// Return root node of subtree
|
||||
return node
|
||||
}
|
||||
|
||||
/* Search node */
|
||||
fun search(_val: Int): TreeNode? {
|
||||
var cur = root
|
||||
// Loop search, exit after passing leaf node
|
||||
while (cur != null) {
|
||||
// Target node is in cur's right subtree
|
||||
cur = if (cur._val < _val)
|
||||
cur.right!!
|
||||
// Target node is in cur's left subtree
|
||||
else if (cur._val > _val)
|
||||
cur.left
|
||||
// Found target node, exit loop
|
||||
else
|
||||
break
|
||||
}
|
||||
// Return target node
|
||||
return cur
|
||||
}
|
||||
}
|
||||
|
||||
fun testInsert(tree: AVLTree, _val: Int) {
|
||||
tree.insert(_val)
|
||||
println("\nAfter inserting node $_val, AVL tree is")
|
||||
printTree(tree.root)
|
||||
}
|
||||
|
||||
fun testRemove(tree: AVLTree, _val: Int) {
|
||||
tree.remove(_val)
|
||||
println("\nAfter deleting node $_val, AVL tree is")
|
||||
printTree(tree.root)
|
||||
}
|
||||
|
||||
/* Driver Code */
|
||||
fun main() {
|
||||
/* Please pay attention to how the AVL tree maintains balance after inserting nodes */
|
||||
val avlTree = AVLTree()
|
||||
|
||||
/* Insert node */
|
||||
// Delete nodes
|
||||
testInsert(avlTree, 1)
|
||||
testInsert(avlTree, 2)
|
||||
testInsert(avlTree, 3)
|
||||
testInsert(avlTree, 4)
|
||||
testInsert(avlTree, 5)
|
||||
testInsert(avlTree, 8)
|
||||
testInsert(avlTree, 7)
|
||||
testInsert(avlTree, 9)
|
||||
testInsert(avlTree, 10)
|
||||
testInsert(avlTree, 6)
|
||||
|
||||
/* Please pay attention to how the AVL tree maintains balance after deleting nodes */
|
||||
testInsert(avlTree, 7)
|
||||
|
||||
/* Remove node */
|
||||
// Delete node with degree 1
|
||||
testRemove(avlTree, 8) // Delete node with degree 2
|
||||
testRemove(avlTree, 5) // Remove node with degree 1
|
||||
testRemove(avlTree, 4) // Remove node with degree 2
|
||||
|
||||
/* Search node */
|
||||
val node = avlTree.search(7)
|
||||
println("\n Found node object is $node, node value = ${node?._val}")
|
||||
}
|
||||
@@ -0,0 +1,157 @@
|
||||
/**
|
||||
* File: binary_search_tree.kt
|
||||
* Created Time: 2024-01-25
|
||||
* Author: curtishd (1023632660@qq.com)
|
||||
*/
|
||||
|
||||
package chapter_tree
|
||||
|
||||
import utils.TreeNode
|
||||
import utils.printTree
|
||||
|
||||
/* Binary search tree */
|
||||
class BinarySearchTree {
|
||||
// Initialize empty tree
|
||||
private var root: TreeNode? = null
|
||||
|
||||
/* Get binary tree root node */
|
||||
fun getRoot(): TreeNode? {
|
||||
return root
|
||||
}
|
||||
|
||||
/* Search node */
|
||||
fun search(num: Int): TreeNode? {
|
||||
var cur = root
|
||||
// Loop search, exit after passing leaf node
|
||||
while (cur != null) {
|
||||
// Target node is in cur's right subtree
|
||||
cur = if (cur._val < num)
|
||||
cur.right
|
||||
// Target node is in cur's left subtree
|
||||
else if (cur._val > num)
|
||||
cur.left
|
||||
// Found target node, exit loop
|
||||
else
|
||||
break
|
||||
}
|
||||
// Return target node
|
||||
return cur
|
||||
}
|
||||
|
||||
/* Insert node */
|
||||
fun insert(num: Int) {
|
||||
// If tree is empty, initialize root node
|
||||
if (root == null) {
|
||||
root = TreeNode(num)
|
||||
return
|
||||
}
|
||||
var cur = root
|
||||
var pre: TreeNode? = null
|
||||
// Loop search, exit after passing leaf node
|
||||
while (cur != null) {
|
||||
// Found duplicate node, return directly
|
||||
if (cur._val == num)
|
||||
return
|
||||
pre = cur
|
||||
// Insertion position is in cur's right subtree
|
||||
cur = if (cur._val < num)
|
||||
cur.right
|
||||
// Insertion position is in cur's left subtree
|
||||
else
|
||||
cur.left
|
||||
}
|
||||
// Insert node
|
||||
val node = TreeNode(num)
|
||||
if (pre?._val!! < num)
|
||||
pre.right = node
|
||||
else
|
||||
pre.left = node
|
||||
}
|
||||
|
||||
/* Remove node */
|
||||
fun remove(num: Int) {
|
||||
// If tree is empty, return directly
|
||||
if (root == null)
|
||||
return
|
||||
var cur = root
|
||||
var pre: TreeNode? = null
|
||||
// Loop search, exit after passing leaf node
|
||||
while (cur != null) {
|
||||
// Found node to delete, exit loop
|
||||
if (cur._val == num)
|
||||
break
|
||||
pre = cur
|
||||
// Node to delete is in cur's right subtree
|
||||
cur = if (cur._val < num)
|
||||
cur.right
|
||||
// Node to delete is in cur's left subtree
|
||||
else
|
||||
cur.left
|
||||
}
|
||||
// If no node to delete, return directly
|
||||
if (cur == null)
|
||||
return
|
||||
// Number of child nodes = 0 or 1
|
||||
if (cur.left == null || cur.right == null) {
|
||||
// When number of child nodes = 0 / 1, child = null / that child node
|
||||
val child = if (cur.left != null)
|
||||
cur.left
|
||||
else
|
||||
cur.right
|
||||
// Delete node cur
|
||||
if (cur != root) {
|
||||
if (pre!!.left == cur)
|
||||
pre.left = child
|
||||
else
|
||||
pre.right = child
|
||||
} else {
|
||||
// If deleted node is root node, reassign root node
|
||||
root = child
|
||||
}
|
||||
// Number of child nodes = 2
|
||||
} else {
|
||||
// Get next node of cur in inorder traversal
|
||||
var tmp = cur.right
|
||||
while (tmp!!.left != null) {
|
||||
tmp = tmp.left
|
||||
}
|
||||
// Recursively delete node tmp
|
||||
remove(tmp._val)
|
||||
// Replace cur with tmp
|
||||
cur._val = tmp._val
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Driver Code */
|
||||
fun main() {
|
||||
/* Initialize binary search tree */
|
||||
val bst = BinarySearchTree()
|
||||
// Please note that different insertion orders will generate different binary trees, this sequence can generate a perfect binary tree
|
||||
val nums = intArrayOf(8, 4, 12, 2, 6, 10, 14, 1, 3, 5, 7, 9, 11, 13, 15)
|
||||
for (num in nums) {
|
||||
bst.insert(num)
|
||||
}
|
||||
println("\nInitialized binary tree is\n")
|
||||
printTree(bst.getRoot())
|
||||
|
||||
/* Search node */
|
||||
val node = bst.search(7)
|
||||
println("Found node object is $node, node value = ${node?._val}")
|
||||
|
||||
/* Insert node */
|
||||
bst.insert(16)
|
||||
println("\nAfter inserting node 16, binary tree is\n")
|
||||
printTree(bst.getRoot())
|
||||
|
||||
/* Remove node */
|
||||
bst.remove(1)
|
||||
println("\nAfter removing node 1, binary tree is\n")
|
||||
printTree(bst.getRoot())
|
||||
bst.remove(2)
|
||||
println("\nAfter removing node 2, binary tree is\n")
|
||||
printTree(bst.getRoot())
|
||||
bst.remove(4)
|
||||
println("\nAfter removing node 4, binary tree is\n")
|
||||
printTree(bst.getRoot())
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
/**
|
||||
* File: binary_tree.kt
|
||||
* Created Time: 2024-01-25
|
||||
* Author: curtishd (1023632660@qq.com)
|
||||
*/
|
||||
|
||||
package chapter_tree
|
||||
|
||||
import utils.TreeNode
|
||||
import utils.printTree
|
||||
|
||||
/* Driver Code */
|
||||
fun main() {
|
||||
/* Initialize binary tree */
|
||||
// Initialize nodes
|
||||
val n1 = TreeNode(1)
|
||||
val n2 = TreeNode(2)
|
||||
val n3 = TreeNode(3)
|
||||
val n4 = TreeNode(4)
|
||||
val n5 = TreeNode(5)
|
||||
// Build references (pointers) between nodes
|
||||
n1.left = n2
|
||||
n1.right = n3
|
||||
n2.left = n4
|
||||
n2.right = n5
|
||||
println("\nInitialize binary tree\n")
|
||||
printTree(n1)
|
||||
|
||||
/* Insert node P between n1 -> n2 */
|
||||
val P = TreeNode(0)
|
||||
// Delete node
|
||||
n1.left = P
|
||||
P.left = n2
|
||||
println("\nAfter inserting node P\n")
|
||||
printTree(n1)
|
||||
// Remove node P
|
||||
n1.left = n2
|
||||
println("\nAfter removing node P\n")
|
||||
printTree(n1)
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
/**
|
||||
* File: binary_tree_bfs.kt
|
||||
* Created Time: 2024-01-25
|
||||
* Author: curtishd (1023632660@qq.com)
|
||||
*/
|
||||
|
||||
package chapter_tree
|
||||
|
||||
import utils.TreeNode
|
||||
import utils.printTree
|
||||
import java.util.*
|
||||
|
||||
/* Level-order traversal */
|
||||
fun levelOrder(root: TreeNode?): MutableList<Int> {
|
||||
// Initialize queue, add root node
|
||||
val queue = LinkedList<TreeNode?>()
|
||||
queue.add(root)
|
||||
// Initialize a list to save the traversal sequence
|
||||
val list = mutableListOf<Int>()
|
||||
while (queue.isNotEmpty()) {
|
||||
val node = queue.poll() // Dequeue
|
||||
list.add(node?._val!!) // Save node value
|
||||
if (node.left != null)
|
||||
queue.offer(node.left) // Left child node enqueue
|
||||
if (node.right != null)
|
||||
queue.offer(node.right) // Right child node enqueue
|
||||
}
|
||||
return list
|
||||
}
|
||||
|
||||
/* Driver Code */
|
||||
fun main() {
|
||||
/* Initialize binary tree */
|
||||
// Here we use a function to generate binary tree directly from list
|
||||
val root = TreeNode.listToTree(mutableListOf(1, 2, 3, 4, 5, 6, 7))
|
||||
println("\nInitialize binary tree\n")
|
||||
printTree(root)
|
||||
|
||||
/* Level-order traversal */
|
||||
val list = levelOrder(root)
|
||||
println("\nLevel-order traversal node print sequence = $list")
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
/**
|
||||
* File: binary_tree_dfs.kt
|
||||
* Created Time: 2024-01-25
|
||||
* Author: curtishd (1023632660@qq.com)
|
||||
*/
|
||||
|
||||
package chapter_tree
|
||||
|
||||
import utils.TreeNode
|
||||
import utils.printTree
|
||||
|
||||
// Initialize list for storing traversal sequence
|
||||
var list = mutableListOf<Int>()
|
||||
|
||||
/* Preorder traversal */
|
||||
fun preOrder(root: TreeNode?) {
|
||||
if (root == null) return
|
||||
// Visit priority: root node -> left subtree -> right subtree
|
||||
list.add(root._val)
|
||||
preOrder(root.left)
|
||||
preOrder(root.right)
|
||||
}
|
||||
|
||||
/* Inorder traversal */
|
||||
fun inOrder(root: TreeNode?) {
|
||||
if (root == null) return
|
||||
// Visit priority: left subtree -> root node -> right subtree
|
||||
inOrder(root.left)
|
||||
list.add(root._val)
|
||||
inOrder(root.right)
|
||||
}
|
||||
|
||||
/* Postorder traversal */
|
||||
fun postOrder(root: TreeNode?) {
|
||||
if (root == null) return
|
||||
// Visit priority: left subtree -> right subtree -> root node
|
||||
postOrder(root.left)
|
||||
postOrder(root.right)
|
||||
list.add(root._val)
|
||||
}
|
||||
|
||||
/* Driver Code */
|
||||
fun main() {
|
||||
/* Initialize binary tree */
|
||||
// Here we use a function to generate binary tree directly from list
|
||||
val root = TreeNode.listToTree(mutableListOf(1, 2, 3, 4, 5, 6, 7))
|
||||
println("\nInitialize binary tree\n")
|
||||
printTree(root)
|
||||
|
||||
/* Preorder traversal */
|
||||
list.clear()
|
||||
preOrder(root)
|
||||
println("\nPre-order traversal node print sequence = $list")
|
||||
|
||||
/* Inorder traversal */
|
||||
list.clear()
|
||||
inOrder(root)
|
||||
println("\nIn-order traversal node print sequence = $list")
|
||||
|
||||
/* Postorder traversal */
|
||||
list.clear()
|
||||
postOrder(root)
|
||||
println("\nPost-order traversal node print sequence = $list")
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
/**
|
||||
* File: ListNode.kt
|
||||
* Created Time: 2024-01-25
|
||||
* Author: curtishd (1023632660@qq.com)
|
||||
*/
|
||||
|
||||
package utils
|
||||
|
||||
/* Linked list node */
|
||||
class ListNode(var _val: Int) {
|
||||
var next: ListNode? = null
|
||||
|
||||
companion object {
|
||||
/* Deserialize a list into a linked list */
|
||||
fun arrToLinkedList(arr: IntArray): ListNode? {
|
||||
val dum = ListNode(0)
|
||||
var head = dum
|
||||
for (_val in arr) {
|
||||
head.next = ListNode(_val)
|
||||
head = head.next!!
|
||||
}
|
||||
return dum.next
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,107 @@
|
||||
/**
|
||||
* File: PrintUtil.kt
|
||||
* Created Time: 2024-01-25
|
||||
* Author: curtishd (1023632660@qq.com)
|
||||
*/
|
||||
|
||||
package utils
|
||||
|
||||
import java.util.*
|
||||
|
||||
class Trunk(var prev: Trunk?, var str: String)
|
||||
|
||||
/* Print matrix (Array) */
|
||||
fun <T> printMatrix(matrix: Array<Array<T>>) {
|
||||
println("[")
|
||||
for (row in matrix) {
|
||||
println(" $row,")
|
||||
}
|
||||
println("]")
|
||||
}
|
||||
|
||||
/* Print matrix (List) */
|
||||
fun <T> printMatrix(matrix: MutableList<MutableList<T>>) {
|
||||
println("[")
|
||||
for (row in matrix) {
|
||||
println(" $row,")
|
||||
}
|
||||
println("]")
|
||||
}
|
||||
|
||||
/* Print linked list */
|
||||
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 = " -> "))
|
||||
}
|
||||
|
||||
/* Print binary tree */
|
||||
fun printTree(root: TreeNode?) {
|
||||
printTree(root, null, false)
|
||||
}
|
||||
|
||||
/**
|
||||
* Print binary tree
|
||||
* This tree printer is borrowed from TECHIE DELIGHT
|
||||
* https://www.techiedelight.com/c-program-print-binary-tree/
|
||||
*/
|
||||
fun printTree(root: TreeNode?, prev: Trunk?, isRight: Boolean) {
|
||||
if (root == null) {
|
||||
return
|
||||
}
|
||||
|
||||
var prevStr = " "
|
||||
val trunk = Trunk(prev, prevStr)
|
||||
|
||||
printTree(root.right, trunk, true)
|
||||
|
||||
if (prev == null) {
|
||||
trunk.str = "———"
|
||||
} else if (isRight) {
|
||||
trunk.str = "/———"
|
||||
prevStr = " |"
|
||||
} else {
|
||||
trunk.str = "\\———"
|
||||
prev.str = prevStr
|
||||
}
|
||||
|
||||
showTrunks(trunk)
|
||||
println(" ${root._val}")
|
||||
|
||||
if (prev != null) {
|
||||
prev.str = prevStr
|
||||
}
|
||||
trunk.str = " |"
|
||||
|
||||
printTree(root.left, trunk, false)
|
||||
}
|
||||
|
||||
fun showTrunks(p: Trunk?) {
|
||||
if (p == null) {
|
||||
return
|
||||
}
|
||||
showTrunks(p.prev)
|
||||
print(p.str)
|
||||
}
|
||||
|
||||
/* Print hash table */
|
||||
fun <K, V> printHashMap(map: Map<K, V>) {
|
||||
for ((key, value) in map) {
|
||||
println("${key.toString()} -> $value")
|
||||
}
|
||||
}
|
||||
|
||||
/* Print heap */
|
||||
fun printHeap(queue: Queue<Int>?) {
|
||||
val list = mutableListOf<Int?>()
|
||||
queue?.let { list.addAll(it) }
|
||||
print("Heap array representation:")
|
||||
println(list)
|
||||
println("Heap tree representation:")
|
||||
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
|
||||
|
||||
/* Binary tree node class */
|
||||
/* Constructor */
|
||||
class TreeNode(
|
||||
var _val: Int // Node value
|
||||
) {
|
||||
var height: Int = 0 // Node height
|
||||
var left: TreeNode? = null // Reference to left child node
|
||||
var right: TreeNode? = null // Reference to right child node
|
||||
|
||||
// For the serialization encoding rules, please refer to:
|
||||
// https://www.hello-algo.com/chapter_tree/array_representation_of_tree/
|
||||
// Array representation of binary tree:
|
||||
// [1, 2, 3, 4, None, 6, 7, 8, 9, None, None, 12, None, None, 15]
|
||||
// Linked list representation of binary tree:
|
||||
// /——— 15
|
||||
// /——— 7
|
||||
// /——— 3
|
||||
// | \——— 6
|
||||
// | \——— 12
|
||||
// ——— 1
|
||||
// \——— 2
|
||||
// | /——— 9
|
||||
// \——— 4
|
||||
// \——— 8
|
||||
|
||||
/* Deserialize a list into a binary tree: recursion */
|
||||
companion object {
|
||||
private fun listToTreeDFS(arr: MutableList<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
|
||||
}
|
||||
|
||||
/* Deserialize a list into a binary tree */
|
||||
fun listToTree(arr: MutableList<Int?>): TreeNode? {
|
||||
return listToTreeDFS(arr, 0)
|
||||
}
|
||||
|
||||
/* Serialize a binary tree into a list: recursion */
|
||||
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)
|
||||
}
|
||||
|
||||
/* Serialize a binary tree into a list */
|
||||
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
|
||||
|
||||
/* Vertex class */
|
||||
class Vertex(val _val: Int) {
|
||||
companion object {
|
||||
/* Input value list vals, return vertex list 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
|
||||
}
|
||||
|
||||
/* Input vertex list vets, return value list 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