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:
Yudong Jin
2025-12-31 07:44:52 +08:00
committed by GitHub
parent 45e1295241
commit 2778a6f9c7
1284 changed files with 71557 additions and 3275 deletions
@@ -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")
}
+66
View File
@@ -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)
}
+160
View File
@@ -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")
}
+38
View File
@@ -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")
}
+223
View File
@@ -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")
}
+25
View File
@@ -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
}
}
}
+107
View File
@@ -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)
}
+69
View File
@@ -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
}
}
}
+30
View File
@@ -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
}
}
}