diff --git a/README.md b/README.md
index 957511155d..d0ead87d6b 100644
--- a/README.md
+++ b/README.md
@@ -35,6 +35,7 @@
+
diff --git a/codes/lua/chapter_array_and_linkedlist/array.lua b/codes/lua/chapter_array_and_linkedlist/array.lua
new file mode 100644
index 0000000000..519dee5b02
--- /dev/null
+++ b/codes/lua/chapter_array_and_linkedlist/array.lua
@@ -0,0 +1,131 @@
+-- @script array.lua
+-- @date 2025-11-11
+-- @author fisheryv (yue.fisher2025@gdhfi.com)
+
+
+--- 随机访问元素
+--- @param nums table 数组
+--- @return number 随机访问到的元素
+local function random_access(nums)
+ -- 在区间 [1, #nums] 中随机抽取一个数字
+ local random_index = math.random(1, #nums)
+ -- 获取并返回随机元素
+ local random_num = nums[random_index]
+ return random_num
+end
+
+--- 扩展数组长度
+--- 请注意,Lua 的 table 是动态的,可以直接扩展
+--- 为了方便学习,本函数将 table 看作长度不可变的数组
+--- @param nums table 数组
+--- @param enlarge number 扩展的长度
+--- @return table 扩展后的新数组
+local function extend(nums, enlarge)
+ -- 初始化一个扩展长度后的数组
+ local res = {}
+ -- 将原数组中的所有元素复制到新数组
+ for i = 1, #nums do
+ res[i] = nums[i]
+ end
+ -- 填充新增的位置为0
+ for i = #nums + 1, #nums + enlarge do
+ res[i] = 0
+ end
+ -- 返回扩展后的新数组
+ return res
+end
+
+--- 在数组的索引 index 处插入元素 num
+--- @param nums table 数组
+--- @param num number 要插入的元素
+--- @param index number 插入位置的索引
+local function insert(nums, num, index)
+ -- 把索引 index 以及之后的所有元素向后移动一位
+ for i = #nums, index, -1 do
+ nums[i + 1] = nums[i]
+ end
+ -- 将 num 赋给 index 处的元素
+ nums[index] = num
+end
+
+--- 删除索引 index 处的元素
+--- @param nums table 数组
+--- @param index number 要删除元素的索引
+local function remove(nums, index)
+ -- 把索引 index 之后的所有元素向前移动一位
+ for i = index, #nums - 1 do
+ nums[i] = nums[i + 1]
+ end
+ -- 将最后一个元素设为nil
+ nums[#nums] = nil
+end
+
+--- 遍历数组
+--- @param nums table 数组
+local function traverse(nums)
+ local count = 0
+ -- 通过索引遍历数组
+ for i = 1, #nums do
+ count = count + nums[i]
+ end
+ -- 直接遍历数组元素
+ for _, num in ipairs(nums) do
+ count = count + num
+ end
+ -- 同时遍历数据索引和元素(在 Lua 中 ipairs 已经提供了这个功能)
+ for i, num in ipairs(nums) do
+ count = count + nums[i]
+ count = count + num
+ end
+end
+
+--- 在数组中查找指定元素
+--- @param nums table 数组
+--- @param target number 要查找的元素
+--- @return number 元素的索引,未找到则返回 -1
+local function find(nums, target)
+ for i = 1, #nums do
+ if nums[i] == target then
+ return i
+ end
+ end
+ return -1
+end
+
+-- Driver Code
+local function main()
+ -- 初始化数组
+ local arr = { 0, 0, 0, 0, 0 }
+ print("数组 arr = [" .. table.concat(arr, ", ") .. "]")
+ local nums = { 1, 3, 2, 5, 4 }
+ print("数组 nums = [" .. table.concat(nums, ", ") .. "]")
+
+ -- 随机访问
+ local random_num = random_access(nums)
+ print("在 nums 中获取随机元素 " .. random_num)
+
+ -- 长度扩展
+ nums = extend(nums, 3)
+ print("将数组长度扩展至 8 ,得到 nums = [" .. table.concat(nums, ", ") .. "]")
+
+ -- 插入元素
+ -- 注意:lua 数组索引从 1 开始,所以其他语言的索引 3 处对应 lua 的索引 4
+ insert(nums, 6, 4)
+ print("在索引 4 处插入数字 6 ,得到 nums = [" .. table.concat(nums, ", ") .. "]")
+
+ -- 删除元素
+ -- 注意:lua 数组索引从 1 开始,所以其他语言的索引 2 处对应 lua 的索引 3
+ remove(nums, 3)
+ print("删除索引 3 处的元素,得到 nums = [" .. table.concat(nums, ", ") .. "]")
+
+ -- 遍历数组
+ traverse(nums)
+
+ -- 查找元素
+ -- 注意:lua 数组索引从 1 开始,所以元素 3 的索引为 2
+ local index = find(nums, 3)
+ print("在 nums 中查找元素 3 ,得到索引 = " .. index)
+end
+
+-- 执行主函数
+main()
diff --git a/codes/lua/chapter_array_and_linkedlist/linked_list.lua b/codes/lua/chapter_array_and_linkedlist/linked_list.lua
new file mode 100644
index 0000000000..511dacc1f1
--- /dev/null
+++ b/codes/lua/chapter_array_and_linkedlist/linked_list.lua
@@ -0,0 +1,117 @@
+-- @script linked_list.lua
+-- @date 2025-11-11
+-- @author fisheryv (yue.fisher2025@gdhfi.com)
+
+
+-- 添加模块搜索路径
+package.path = package.path .. ";codes/lua/modules/?.lua"
+
+local list_node = require("list_node")
+local ListNode = list_node.ListNode
+
+--- 在链表的节点 n0 之后插入节点 P
+--- @param n0 ListNode 要插入位置的前一个节点
+--- @param P ListNode 要插入的节点
+local function insert(n0, P)
+ local n1 = n0.next
+ P.next = n1
+ n0.next = P
+end
+
+--- 删除链表的节点 n0 之后的首个节点
+--- @param n0 ListNode 要删除节点的前一个节点
+local function remove(n0)
+ -- n0 -> P -> n1
+ local P = n0.next
+ if not P then
+ return
+ end
+ local n1 = P.next
+ n0.next = n1
+end
+
+--- 访问链表中索引为 index 的节点
+--- @param head ListNode 链表头节点
+--- @param index integer 要访问的索引
+--- @return ListNode|nil 找到的节点或nil
+local function access(head, index)
+ for _ = 1, index do
+ if not head then
+ return nil
+ end
+ head = head.next
+ end
+ return head
+end
+
+--- 在链表中查找值为 target 的首个节点
+--- @param head ListNode 链表头节点
+--- @param target integer 要查找的目标值
+--- @return number 节点索引,未找到返回-1
+local function find(head, target)
+ local index = 0
+ while head do
+ if head.val == target then
+ return index
+ end
+ head = head.next
+ index = index + 1
+ end
+ return -1
+end
+
+--- 打印链表
+--- @param head ListNode 链表头节点
+local function printLinkedList(head)
+ local current = head
+ local output = {}
+ while current do
+ table.insert(output, current.val)
+ current = current.next
+ end
+ print(table.concat(output, " -> "))
+end
+
+-- Driver Code
+local function main()
+ -- 初始化链表
+ -- 初始化各个节点
+ local n0 = ListNode.new(1)
+ local n1 = ListNode.new(3)
+ local n2 = ListNode.new(2)
+ local n3 = ListNode.new(5)
+ local n4 = ListNode.new(4)
+ -- 构建节点之间的引用
+ n0.next = n1
+ n1.next = n2
+ n2.next = n3
+ n3.next = n4
+ print("初始化的链表为")
+ printLinkedList(n0)
+
+ -- 插入节点
+ local p = ListNode.new(0)
+ insert(n0, p)
+ print("插入节点后的链表为")
+ printLinkedList(n0)
+
+ -- 删除节点
+ remove(n0)
+ print("删除节点后的链表为")
+ printLinkedList(n0)
+
+ -- 访问节点
+ local node = access(n0, 3)
+ if node then
+ print("链表中索引 3 处的节点的值 = " .. node.val)
+ else
+ print("链表中索引 3 处的节点不存在")
+ end
+
+ -- 查找节点
+ local index = find(n0, 2)
+ print("链表中值为 2 的节点的索引 = " .. index)
+end
+
+-- 执行主函数
+main()
diff --git a/codes/lua/chapter_array_and_linkedlist/list.lua b/codes/lua/chapter_array_and_linkedlist/list.lua
new file mode 100644
index 0000000000..dca44fc7e7
--- /dev/null
+++ b/codes/lua/chapter_array_and_linkedlist/list.lua
@@ -0,0 +1,67 @@
+-- @script: array.lua
+-- @date 2025-11-11
+-- @author fisheryv (yue.fisher2025@gdhfi.com)
+
+
+-- Driver Code
+local function main()
+ -- 初始化列表
+ local nums = { 1, 3, 2, 5, 4 }
+ print("\n列表 nums = [" .. table.concat(nums, ", ") .. "]")
+
+ -- 访问元素
+ -- 注意:lua 数组索引从 1 开始,所以其他语言的索引 1 处对应 lua 的索引 2
+ local x = nums[2]
+ print("\n访问索引 2 处的元素,得到 x = " .. x)
+
+ -- 更新元素
+ -- 注意:lua 数组索引从 1 开始,所以其他语言的索引 1 处对应 lua 的索引 2
+ nums[2] = 0
+ print("\n将索引 2 处的元素更新为 0 ,得到 nums = [" .. table.concat(nums, ", ") .. "]")
+
+ -- 清空列表
+ nums = {}
+ print("\n清空列表后 nums = [" .. table.concat(nums, ", ") .. "]")
+
+ -- 在尾部添加元素
+ table.insert(nums, 1)
+ table.insert(nums, 3)
+ table.insert(nums, 2)
+ table.insert(nums, 5)
+ table.insert(nums, 4)
+ print("\n添加元素后 nums = [" .. table.concat(nums, ", ") .. "]")
+
+ -- 在中间插入元素
+ -- 注意:lua 数组索引从 1 开始,所以其他语言的索引 3 处对应 lua 的索引 4
+ table.insert(nums, 4, 6) -- 在索引 4 处插入 6
+ print("\n在索引 4 处插入数字 6 ,得到 nums = [" .. table.concat(nums, ", ") .. "]")
+
+ -- 删除元素
+ -- 注意:lua 数组索引从 1 开始,所以其他语言的索引 3 处对应 lua 的索引 4
+ table.remove(nums, 4) -- 删除索引 4 处的元素
+ print("\n删除索引 4 处的元素,得到 nums = [" .. table.concat(nums, ", ") .. "]")
+
+ -- 通过索引遍历列表
+ local count = 0
+ for i = 1, #nums do
+ count = count + nums[i]
+ end
+ -- 直接遍历列表元素
+ for _, num in ipairs(nums) do
+ count = count + num
+ end
+
+ -- 拼接两个列表
+ local nums1 = { 6, 8, 7, 10, 9 }
+ for _, num in ipairs(nums1) do
+ table.insert(nums, num)
+ end
+ print("\n将列表 nums1 拼接到 nums 之后,得到 nums = [" .. table.concat(nums, ", ") .. "]")
+
+ -- 排序列表
+ table.sort(nums)
+ print("\n排序列表后 nums = [" .. table.concat(nums, ", ") .. "]")
+end
+
+-- 执行主函数
+main()
diff --git a/codes/lua/chapter_array_and_linkedlist/my_list.lua b/codes/lua/chapter_array_and_linkedlist/my_list.lua
new file mode 100644
index 0000000000..68db967850
--- /dev/null
+++ b/codes/lua/chapter_array_and_linkedlist/my_list.lua
@@ -0,0 +1,188 @@
+-- @script my_list.lua
+-- @date 2025-11-11
+-- @author fisheryv (yue.fisher2025@gdhfi.com)
+
+--- @class MyList
+--- 列表类(动态数组实现)
+--- @field _capacity integer 列表容量
+--- @field _arr table 列表数组
+--- @field _size integer 列表长度(当前元素数量)
+--- @field _extend_ratio integer 每次列表扩容的倍数
+local MyList = {}
+MyList.__index = MyList
+
+--- 构造方法
+--- @return MyList 新列表实例
+function MyList:new()
+ local obj = {}
+ setmetatable(obj, {
+ __index = self
+ })
+
+ -- 列表容量
+ obj._capacity = 10
+ -- 数组(存储列表元素)
+ obj._arr = {}
+ for i = 1, obj._capacity do
+ obj._arr[i] = 0
+ end
+ -- 列表长度(当前元素数量)
+ obj._size = 0
+ -- 每次列表扩容的倍数
+ obj._extend_ratio = 2
+
+ return obj
+end
+
+--- 获取列表长度(当前元素数量)
+--- @return integer
+function MyList:size()
+ return self._size
+end
+
+--- 获取列表容量
+--- @return integer
+function MyList:capacity()
+ return self._capacity
+end
+
+--- 访问元素
+--- @param index integer
+--- @return integer
+function MyList:get(index)
+ -- 索引如果越界,则抛出异常,下同
+ if index < 0 or index >= self._size then
+ error("索引越界")
+ end
+ return self._arr[index + 1] -- Lua 数组索引从1开始
+end
+
+--- 更新元素
+--- @param num integer
+--- @param index integer
+function MyList:set(num, index)
+ if index < 0 or index >= self._size then
+ error("索引越界")
+ end
+ self._arr[index + 1] = num -- Lua 数组索引从1开始
+end
+
+--- 在尾部添加元素
+--- @param num integer
+function MyList:add(num)
+ -- 元素数量超出容量时,触发扩容机制
+ if self:size() == self:capacity() then
+ self:extend_capacity()
+ end
+ self._arr[self._size + 1] = num
+ self._size = self._size + 1
+end
+
+--- 在中间插入元素
+--- @param num integer
+--- @param index integer
+function MyList:insert(num, index)
+ if index < 0 or index >= self._size then
+ error("索引越界")
+ end
+ -- 元素数量超出容量时,触发扩容机制
+ if self._size == self:capacity() then
+ self:extend_capacity()
+ end
+ -- 将索引 index 以及之后的元素都向后移动一位
+ for j = self._size, index + 1, -1 do -- Lua 数组索引从1开始
+ self._arr[j + 1] = self._arr[j]
+ end
+ self._arr[index + 1] = num -- Lua 数组索引从1开始
+ -- 更新元素数量
+ self._size = self._size + 1
+end
+
+--- 删除元素
+--- @param index integer
+--- @return integer
+function MyList:remove(index)
+ if index < 0 or index >= self._size then
+ error("索引越界")
+ end
+ local num = self._arr[index + 1] -- Lua 数组索引从1开始
+ -- 将索引 index 之后的元素都向前移动一位
+ for j = index + 1, self._size - 1 do -- Lua 数组索引从1开始
+ self._arr[j] = self._arr[j + 1]
+ end
+ -- 更新元素数量
+ self._size = self._size - 1
+ -- 返回被删除的元素
+ return num
+end
+
+--- 列表扩容
+function MyList:extend_capacity()
+ -- 新建一个长度为原数组 _extend_ratio 倍的新数组,并将原数组复制到新数组
+ local new_capacity = self:capacity() * self._extend_ratio
+ local new_arr = {}
+
+ -- 复制原数组元素
+ for i = 1, self:capacity() do
+ new_arr[i] = self._arr[i]
+ end
+ -- 填充新增容量为0
+ for i = self:capacity() + 1, new_capacity do
+ new_arr[i] = 0
+ end
+
+ self._arr = new_arr
+ -- 更新列表容量
+ self._capacity = new_capacity
+end
+
+--- 返回有效长度的列表
+--- @return table
+function MyList:to_array()
+ local result = {}
+ for i = 1, self._size do
+ result[i] = self._arr[i]
+ end
+ return result
+end
+
+-- Driver Code
+local function main()
+ -- 初始化列表
+ local nums = MyList:new()
+ -- 在尾部添加元素
+ nums:add(1)
+ nums:add(3)
+ nums:add(2)
+ nums:add(5)
+ nums:add(4)
+ print(string.format("列表 nums = [%s] ,容量 = %d ,长度 = %d", table.concat(nums:to_array(), ", "),
+ nums:capacity(), nums:size()))
+
+ -- 在中间插入元素
+ nums:insert(6, 3)
+ print("在索引 3 处插入数字 6 ,得到 nums = [" .. table.concat(nums:to_array(), ", ") .. "]")
+
+ -- 删除元素
+ nums:remove(3)
+ print("删除索引 3 处的元素,得到 nums = [" .. table.concat(nums:to_array(), ", ") .. "]")
+
+ -- 访问元素
+ local num = nums:get(1)
+ print("访问索引 1 处的元素,得到 num = " .. num)
+
+ -- 更新元素
+ nums:set(0, 1)
+ print("将索引 1 处的元素更新为 0 ,得到 nums = [" .. table.concat(nums:to_array(), ", ") .. "]")
+
+ -- 测试扩容机制
+ for i = 0, 9 do
+ -- 在 i = 5 时,列表长度将超出列表容量,此时触发扩容机制
+ nums:add(i)
+ end
+ print(string.format("扩容后的列表 [%s] ,容量 = %d ,长度 = %d", table.concat(nums:to_array(), ", "),
+ nums:capacity(), nums:size()))
+end
+
+-- 执行主函数
+main()
diff --git a/codes/lua/chapter_backtracking/n_queens.lua b/codes/lua/chapter_backtracking/n_queens.lua
new file mode 100644
index 0000000000..5a70ebf7db
--- /dev/null
+++ b/codes/lua/chapter_backtracking/n_queens.lua
@@ -0,0 +1,102 @@
+-- @script n_queens.lua
+-- @date 2025-11-15
+-- @author fisheryv (yue.fisher2025@gdhfi.com)
+
+--- 回溯算法:n 皇后
+--- @param row integer 当前处理的行
+--- @param n integer 棋盘大小
+--- @param state table 当前棋盘状态
+--- @param res table 存储所有解的列表
+--- @param cols table 列占用标记
+--- @param diags1 table 主对角线占用标记
+--- @param diags2 table 次对角线占用标记
+local function backtrack(row, n, state, res, cols, diags1, diags2)
+ -- 当放置完所有行时,记录解
+ if row == n then
+ local solution = {}
+ for i = 1, n do
+ solution[i] = {}
+ for j = 1, n do
+ solution[i][j] = state[i][j]
+ end
+ end
+ table.insert(res, solution)
+ return
+ end
+
+ -- 遍历所有列
+ for col = 1, n do
+ -- 计算该格子对应的主对角线和次对角线
+ local diag1 = row - col + n
+ local diag2 = row + col - 1
+
+ -- 剪枝:不允许该格子所在列、主对角线、次对角线上存在皇后
+ if not cols[col] and not diags1[diag1] and not diags2[diag2] then
+ -- 尝试:将皇后放置在该格子
+ state[row + 1][col] = "Q"
+ cols[col] = true
+ diags1[diag1] = true
+ diags2[diag2] = true
+
+ -- 放置下一行
+ backtrack(row + 1, n, state, res, cols, diags1, diags2)
+
+ -- 回退:将该格子恢复为空位
+ state[row + 1][col] = "#"
+ cols[col] = false
+ diags1[diag1] = false
+ diags2[diag2] = false
+ end
+ end
+end
+
+--- 求解 n 皇后问题
+--- @param n integer 棋盘大小
+--- @return table 所有解的列表
+local function n_queens(n)
+ -- 初始化 n*n 大小的棋盘,其中 'Q' 代表皇后,'#' 代表空位
+ local state = {}
+ for i = 1, n do
+ state[i] = {}
+ for j = 1, n do
+ state[i][j] = "#"
+ end
+ end
+
+ local cols = {} -- 记录列是否有皇后
+ local diags1 = {} -- 记录主对角线上是否有皇后
+ local diags2 = {} -- 记录次对角线上是否有皇后
+
+ for i = 1, n do
+ cols[i] = false
+ end
+
+ for i = 1, 2 * n - 1 do
+ diags1[i] = false
+ diags2[i] = false
+ end
+
+ local res = {}
+ backtrack(0, n, state, res, cols, diags1, diags2)
+
+ return res
+end
+
+-- Driver Code
+local function main()
+ local n = 4
+ local res = n_queens(n)
+
+ print(string.format("输入棋盘长宽为 %d", n))
+ print(string.format("皇后放置方案共有 %d 种", #res))
+
+ for i, state in ipairs(res) do
+ print("--------------------")
+ for j = 1, #state do
+ print("['" .. table.concat(state[j], "', '") .. "']")
+ end
+ end
+end
+
+-- 执行主函数
+main()
diff --git a/codes/lua/chapter_backtracking/permutations_i.lua b/codes/lua/chapter_backtracking/permutations_i.lua
new file mode 100644
index 0000000000..2e602909d3
--- /dev/null
+++ b/codes/lua/chapter_backtracking/permutations_i.lua
@@ -0,0 +1,70 @@
+-- @script permutations_i.lua
+-- @date 2025-11-15
+-- @author fisheryv (yue.fisher2025@gdhfi.com)
+
+--- 回溯算法:全排列 I
+--- @param state table 当前状态
+--- @param choices table 所有可选元素
+--- @param selected table 元素是否已被选择
+--- @param res table 存储所有结果的表
+local function backtrack(state, choices, selected, res)
+ -- 当状态长度等于元素数量时,记录解
+ if #state == #choices then
+ -- 创建当前状态的副本并添加到结果中
+ local state_copy = {}
+ for i, v in ipairs(state) do
+ state_copy[i] = v
+ end
+ table.insert(res, state_copy)
+ return
+ end
+
+ -- 遍历所有选择
+ for i, choice in ipairs(choices) do
+ -- 剪枝:不允许重复选择元素
+ if not selected[i] then
+ -- 尝试:做出选择,更新状态
+ selected[i] = true
+ table.insert(state, choice)
+
+ -- 进行下一轮选择
+ backtrack(state, choices, selected, res)
+
+ -- 回退:撤销选择,恢复到之前的状态
+ selected[i] = false
+ table.remove(state)
+ end
+ end
+end
+
+--- 全排列 I
+--- @param nums table 输入数组
+--- @return table 所有排列的结果
+local function permutations_i(nums)
+ local res = {}
+ local selected = {}
+
+ -- 初始化selected表
+ for i = 1, #nums do
+ selected[i] = false
+ end
+
+ backtrack({}, nums, selected, res)
+ return res
+end
+
+-- Driver Code
+local function main()
+ local nums = { 1, 2, 3 }
+ local res = permutations_i(nums)
+
+ print(string.format("输入数组 nums = [%s]", table.concat(nums, ", ")))
+ print("所有排列 res: [")
+ for _, permutation in ipairs(res) do
+ print(string.format(" [%s],", table.concat(permutation, ", ")))
+ end
+ print("]")
+end
+
+-- 执行主函数
+main()
diff --git a/codes/lua/chapter_backtracking/permutations_ii.lua b/codes/lua/chapter_backtracking/permutations_ii.lua
new file mode 100644
index 0000000000..fffd30e58c
--- /dev/null
+++ b/codes/lua/chapter_backtracking/permutations_ii.lua
@@ -0,0 +1,74 @@
+-- @script permutations_ii.lua
+-- @date 2025-11-15
+-- @author fisheryv (yue.fisher2025@gdhfi.com)
+
+--- 回溯算法:全排列 II
+--- @function backtrack
+--- @param state table 当前状态(已选择元素)
+--- @param choices table 所有可选元素
+--- @param selected table 标记元素是否已被选择
+--- @param res table 存储所有结果的表
+local function backtrack(state, choices, selected, res)
+ -- 当状态长度等于元素数量时,记录解
+ if #state == #choices then
+ -- 创建当前状态的副本并添加到结果中
+ local state_copy = {}
+ for i, v in ipairs(state) do
+ state_copy[i] = v
+ end
+ table.insert(res, state_copy)
+ return
+ end
+
+ -- 遍历所有选择
+ local duplicated = {} -- 用于记录当前层级已经选择过的元素值
+ for i, choice in ipairs(choices) do
+ -- 剪枝:不允许重复选择元素 且 不允许重复选择相等元素
+ if not selected[i] and not duplicated[choice] then
+ -- 尝试:做出选择,更新状态
+ duplicated[choice] = true -- 记录选择过的元素值
+ selected[i] = true
+ table.insert(state, choice)
+
+ -- 进行下一轮选择
+ backtrack(state, choices, selected, res)
+
+ -- 回退:撤销选择,恢复到之前的状态
+ selected[i] = false
+ table.remove(state)
+ end
+ end
+end
+
+--- 全排列 II
+--- @param nums table 输入数组
+--- @return table 包含所有不重复排列的二维数组
+local function permutations_ii(nums)
+ local res = {}
+ local selected = {}
+
+ -- 初始化selected表
+ for i = 1, #nums do
+ selected[i] = false
+ end
+
+ backtrack({}, nums, selected, res)
+ return res
+end
+
+-- Driver Code
+local function main()
+ local nums = { 1, 2, 2 }
+
+ local res = permutations_ii(nums)
+
+ print(string.format("输入数组 nums = [%s]", table.concat(nums, ", ")))
+ print("所有排列 res = [")
+ for _, permutation in ipairs(res) do
+ print(string.format(" [%s],", table.concat(permutation, ", ")))
+ end
+ print("]")
+end
+
+-- 执行主函数
+main()
diff --git a/codes/lua/chapter_backtracking/preorder_traversal_i_compact.lua b/codes/lua/chapter_backtracking/preorder_traversal_i_compact.lua
new file mode 100644
index 0000000000..ffa0985f15
--- /dev/null
+++ b/codes/lua/chapter_backtracking/preorder_traversal_i_compact.lua
@@ -0,0 +1,50 @@
+-- @script preorder_traversal_i_compact.lua
+-- @date 2025-11-15
+-- @author fisheryv (yue.fisher2025@gdhfi.com)
+
+
+-- 添加模块搜索路径
+package.path = package.path .. ";codes/lua/modules/?.lua"
+
+local print_util = require("print_util")
+local tree_node = require("tree_node")
+local list_to_tree = tree_node.list_to_tree
+
+--- 前序遍历:例题一
+--- @param root TreeNode|nil 根节点
+--- @param res table 存储结果的表
+local function pre_order(root, res)
+ if not root then
+ return
+ end
+
+ -- 如果节点值为7,则记录到结果表中
+ if root.val == 7 then
+ table.insert(res, root)
+ end
+
+ pre_order(root.left, res)
+ pre_order(root.right, res)
+end
+
+-- Driver Code
+local function main()
+ -- 通过列表构建二叉树
+ local root = list_to_tree({ 1, 7, 3, 4, 5, 6, 7 })
+ print("\n初始化二叉树")
+ print_util.print_tree(root, nil, false)
+
+ -- 前序遍历查找值为7的节点
+ local res = {}
+ pre_order(root, res)
+
+ print("\n输出所有值为 7 的节点")
+ local vals = {}
+ for _, node in ipairs(res) do
+ table.insert(vals, node.val)
+ end
+ print("[" .. table.concat(vals, ", ") .. "]")
+end
+
+-- 执行主函数
+main()
diff --git a/codes/lua/chapter_backtracking/preorder_traversal_ii_compact.lua b/codes/lua/chapter_backtracking/preorder_traversal_ii_compact.lua
new file mode 100644
index 0000000000..b7733790e2
--- /dev/null
+++ b/codes/lua/chapter_backtracking/preorder_traversal_ii_compact.lua
@@ -0,0 +1,66 @@
+-- @script preorder_traversal_ii_compact.lua
+-- @date 2025-11-15
+-- @author fisheryv (yue.fisher2025@gdhfi.com)
+
+
+-- 添加模块搜索路径
+package.path = package.path .. ";codes/lua/modules/?.lua"
+
+local print_util = require("print_util")
+local tree_node = require("tree_node")
+local list_to_tree = tree_node.list_to_tree
+
+--- 前序遍历:例题二
+--- @param root TreeNode|nil
+--- @param path table
+--- @param res table
+local function pre_order(root, path, res)
+ if not root then
+ return
+ end
+
+ -- 尝试:将当前节点加入路径
+ table.insert(path, root)
+
+ -- 找到目标节点,记录路径
+ if root.val == 7 then
+ -- 复制当前路径到结果中
+ local path_copy = {}
+ for _, node in ipairs(path) do
+ table.insert(path_copy, node)
+ end
+ table.insert(res, path_copy)
+ end
+
+ -- 递归遍历左右子树
+ pre_order(root.left, path, res)
+ pre_order(root.right, path, res)
+
+ -- 回退:移除当前节点
+ table.remove(path)
+end
+
+-- Driver Code
+local function main()
+ -- 构建二叉树
+ local root = list_to_tree({ 1, 7, 3, 4, 5, 6, 7 })
+ print("\n初始化二叉树")
+ print_util.print_tree(root, nil, false)
+
+ -- 前序遍历查找路径
+ local path = {}
+ local res = {}
+ pre_order(root, path, res)
+
+ print("\n输出所有根节点到节点 7 的路径")
+ for _, path_nodes in ipairs(res) do
+ local path_vals = {}
+ for _, node in ipairs(path_nodes) do
+ table.insert(path_vals, node.val)
+ end
+ print("[" .. table.concat(path_vals, ", ") .. "]")
+ end
+end
+
+-- 执行主函数
+main()
diff --git a/codes/lua/chapter_backtracking/preorder_traversal_iii_compact.lua b/codes/lua/chapter_backtracking/preorder_traversal_iii_compact.lua
new file mode 100644
index 0000000000..cbb1aa0842
--- /dev/null
+++ b/codes/lua/chapter_backtracking/preorder_traversal_iii_compact.lua
@@ -0,0 +1,67 @@
+-- @script preorder_traversal_iii_compact.lua
+-- @date 2025-11-15
+-- @author fisheryv (yue.fisher2025@gdhfi.com)
+
+
+-- 添加模块搜索路径
+package.path = package.path .. ";codes/lua/modules/?.lua"
+
+local print_util = require("print_util")
+local tree_node = require("tree_node")
+local list_to_tree = tree_node.list_to_tree
+
+--- 前序遍历:例题三
+--- @param root TreeNode|nil 当前遍历的节点
+--- @param path table 当前路径
+--- @param res table 存储所有符合条件的路径
+local function pre_order(root, path, res)
+ -- 剪枝:遇到空节点或值为3的节点则返回
+ if not root or root.val == 3 then
+ return
+ end
+
+ -- 尝试:将当前节点加入路径
+ table.insert(path, root)
+
+ -- 记录解:如果当前节点值为7,则记录当前路径
+ if root.val == 7 then
+ -- 创建路径的副本
+ local path_copy = {}
+ for _, node in ipairs(path) do
+ table.insert(path_copy, node)
+ end
+ table.insert(res, path_copy)
+ end
+
+ -- 递归遍历左右子树
+ pre_order(root.left, path, res)
+ pre_order(root.right, path, res)
+
+ -- 回退:从路径中移除当前节点
+ table.remove(path)
+end
+
+-- Driver Code
+local function main()
+ -- 构建二叉树
+ local root = list_to_tree({ 1, 7, 3, 4, 5, 6, 7 })
+ print("\n初始化二叉树")
+ print_util.print_tree(root, nil, false)
+
+ -- 前序遍历查找路径
+ local path = {}
+ local res = {}
+ pre_order(root, path, res)
+
+ print("\n输出所有根节点到节点 7 的路径,路径中不包含值为 3 的节点")
+ for _, path_nodes in ipairs(res) do
+ local path_vals = {}
+ for _, node in ipairs(path_nodes) do
+ table.insert(path_vals, node.val)
+ end
+ print("[" .. table.concat(path_vals, ", ") .. "]")
+ end
+end
+
+-- 执行主函数
+main()
diff --git a/codes/lua/chapter_backtracking/preorder_traversal_iii_template.lua b/codes/lua/chapter_backtracking/preorder_traversal_iii_template.lua
new file mode 100644
index 0000000000..b8c0e9b1dd
--- /dev/null
+++ b/codes/lua/chapter_backtracking/preorder_traversal_iii_template.lua
@@ -0,0 +1,98 @@
+-- @script preorder_traversal_iii_template.lua
+-- @date 2025-11-15
+-- @author fisheryv (yue.fisher2025@gdhfi.com)
+
+
+-- 添加模块搜索路径
+package.path = package.path .. ";codes/lua/modules/?.lua"
+
+local print_util = require("print_util")
+local tree_node = require("tree_node")
+local list_to_tree = tree_node.list_to_tree
+
+--- 判断当前状态是否为解
+--- @param state table 当前路径状态
+--- @return boolean
+local function is_solution(state)
+ return #state > 0 and state[#state].val == 7
+end
+
+--- 记录解
+--- @param state table 当前路径状态
+--- @param res table 结果集合
+local function record_solution(state, res)
+ local solution = {}
+ for _, node in ipairs(state) do
+ table.insert(solution, node)
+ end
+ table.insert(res, solution)
+end
+
+--- 判断在当前状态下,该选择是否合法
+--- @param state table 当前路径状态
+--- @param choice TreeNode|nil 候选节点
+--- @return boolean
+local function is_valid(state, choice)
+ return choice ~= nil and choice.val ~= 3
+end
+
+--- 更新状态
+--- @param state table 当前路径状态
+--- @param choice TreeNode 选择的节点
+local function make_choice(state, choice)
+ table.insert(state, choice)
+end
+
+--- 恢复状态
+--- @param state table 当前路径状态
+--- @param choice TreeNode 选择的节点
+local function undo_choice(state, choice)
+ table.remove(state)
+end
+
+--- 回溯算法:例题三
+--- @param state table 当前路径状态
+--- @param choices table 候选节点列表
+--- @param res table 结果集合
+local function backtrack(state, choices, res)
+ -- 检查是否为解
+ if is_solution(state) then
+ record_solution(state, res)
+ end
+
+ -- 遍历所有选择
+ for _, choice in ipairs(choices) do
+ -- 剪枝:检查选择是否合法
+ if is_valid(state, choice) then
+ -- 尝试:做出选择,更新状态
+ make_choice(state, choice)
+ -- 进行下一轮选择
+ backtrack(state, { choice.left, choice.right }, res)
+ -- 回退:撤销选择,恢复到之前的状态
+ undo_choice(state, choice)
+ end
+ end
+end
+
+-- Driver Code
+local function main()
+ local root = list_to_tree({ 1, 7, 3, 4, 5, 6, 7 })
+ print("\n初始化二叉树")
+ print_util.print_tree(root, nil, false)
+
+ -- 回溯算法
+ local res = {}
+ backtrack({}, { root }, res)
+
+ print("\n输出所有根节点到节点 7 的路径,要求路径中不包含值为 3 的节点")
+ for _, path in ipairs(res) do
+ local path_vals = {}
+ for _, node in ipairs(path) do
+ table.insert(path_vals, node.val)
+ end
+ print("[" .. table.concat(path_vals, ", ") .. "]")
+ end
+end
+
+-- 执行主函数
+main()
diff --git a/codes/lua/chapter_backtracking/subset_sum_i.lua b/codes/lua/chapter_backtracking/subset_sum_i.lua
new file mode 100644
index 0000000000..94572b84aa
--- /dev/null
+++ b/codes/lua/chapter_backtracking/subset_sum_i.lua
@@ -0,0 +1,70 @@
+-- @script subset_sum_i.lua
+-- @date 2025-11-15
+-- @author fisheryv (yue.fisher2025@gdhfi.com)
+
+--- 回溯算法:子集和 I
+--- @param state table 当前状态(子集)
+--- @param target integer 目标和
+--- @param choices table 选择列表
+--- @param start integer 遍历起始索引
+--- @param res table 结果列表
+local function backtrack(state, target, choices, start, res)
+ -- 子集和等于 target 时,记录解
+ if target == 0 then
+ -- 深拷贝当前状态到结果中
+ local copy = {}
+ for i = 1, #state do
+ copy[i] = state[i]
+ end
+ table.insert(res, copy)
+ return
+ end
+
+ -- 遍历所有选择
+ -- 剪枝二:从 start 开始遍历,避免生成重复子集
+ for i = start, #choices do
+ -- 剪枝一:若子集和超过 target,则直接结束循环
+ -- 这是因为数组已排序,后边元素更大,子集和一定超过 target
+ if target - choices[i] < 0 then
+ break
+ end
+
+ -- 尝试:做出选择,更新 target, start
+ table.insert(state, choices[i])
+ -- 进行下一轮选择
+ backtrack(state, target - choices[i], choices, i, res)
+ -- 回退:撤销选择,恢复到之前的状态
+ table.remove(state)
+ end
+end
+
+--- 求解子集和 I
+--- @param nums table 输入数组
+--- @param target integer 目标和
+--- @return table 所有和等于 target 的子集
+local function subset_sum_i(nums, target)
+ local state = {} -- 状态(子集)
+ table.sort(nums) -- 对 nums 进行排序
+ local start = 1 -- 遍历起始点(Lua索引从1开始)
+ local res = {} -- 结果列表(子集列表)
+ backtrack(state, target, nums, start, res)
+ return res
+end
+
+-- Driver Code
+local function main()
+ local nums = { 3, 4, 5 }
+ local target = 9
+ local res = subset_sum_i(nums, target)
+
+ print(string.format("输入数组 nums = [%s], target = %d",
+ table.concat(nums, ", "), target))
+ print(string.format("所有和等于 %d 的子集 res = [", target))
+ for _, subset in ipairs(res) do
+ print(string.format(" [%s],", table.concat(subset, ", ")))
+ end
+ print("]")
+end
+
+-- 执行主函数
+main()
diff --git a/codes/lua/chapter_backtracking/subset_sum_i_naive.lua b/codes/lua/chapter_backtracking/subset_sum_i_naive.lua
new file mode 100644
index 0000000000..36036f35dd
--- /dev/null
+++ b/codes/lua/chapter_backtracking/subset_sum_i_naive.lua
@@ -0,0 +1,70 @@
+-- @script subset_sum_i_naive.lua
+-- @date 2025-11-15
+-- @author fisheryv (yue.fisher2025@gdhfi.com)
+
+--- 回溯算法:子集和 I
+--- @param state table 当前状态(子集)
+--- @param target integer 目标值
+--- @param total integer 当前子集和
+--- @param choices table 可选数字列表
+--- @param res table 结果列表
+local function backtrack(state, target, total, choices, res)
+ -- 子集和等于 target 时,记录解
+ if total == target then
+ local solution = {}
+ for i = 1, #state do
+ solution[i] = state[i]
+ end
+ table.insert(res, solution)
+ return
+ end
+
+ -- 遍历所有选择
+ for i = 1, #choices do
+ -- 剪枝:若子集和超过 target ,则跳过该选择
+ if total + choices[i] > target then
+ -- Lua 没有 continue 关键字,改用if-else结构
+ else
+ -- 尝试:做出选择,更新元素和 total
+ table.insert(state, choices[i])
+
+ -- 进行下一轮选择
+ backtrack(state, target, total + choices[i], choices, res)
+
+ -- 回退:撤销选择,恢复到之前的状态
+ table.remove(state)
+ end
+ end
+end
+
+--- 求解子集和 I(包含重复子集)
+--- @param nums table 输入数组
+--- @param target integer 目标值
+--- @return table 所有和等于 target 的子集
+local function subset_sum_i_naive(nums, target)
+ local state = {} -- 状态(子集)
+ local total = 0 -- 子集和
+ local res = {} -- 结果列表(子集列表)
+ backtrack(state, target, total, nums, res)
+ return res
+end
+
+-- Driver Code
+local function main()
+ local nums = { 3, 4, 5 }
+ local target = 9
+ local res = subset_sum_i_naive(nums, target)
+
+ print(string.format("输入数组 nums = [%s], target = %d", table.concat(nums, ", "), target))
+ print(string.format("所有和等于 %d 的子集 res: [", target))
+
+ for _, subset in ipairs(res) do
+ print(string.format(" [%s],", table.concat(subset, ", ")))
+ end
+ print("]")
+
+ print("请注意,该方法输出的结果包含重复集合")
+end
+
+-- 执行主函数
+main()
diff --git a/codes/lua/chapter_backtracking/subset_sum_ii.lua b/codes/lua/chapter_backtracking/subset_sum_ii.lua
new file mode 100644
index 0000000000..8ca8f7930a
--- /dev/null
+++ b/codes/lua/chapter_backtracking/subset_sum_ii.lua
@@ -0,0 +1,75 @@
+-- @script subset_sum_ii.lua
+-- @date 2025-11-15
+-- @author fisheryv (yue.fisher2025@gdhfi.com)
+
+--- 回溯算法:子集和 II
+--- @param state table 当前状态(子集)
+--- @param target number 目标和
+--- @param choices table 候选数字列表
+--- @param start number 遍历起始位置
+--- @param res table 结果列表
+local function backtrack(state, target, choices, start, res)
+ -- 子集和等于 target 时,记录解
+ if target == 0 then
+ -- 创建当前状态的副本并添加到结果中
+ local state_copy = {}
+ for i = 1, #state do
+ state_copy[i] = state[i]
+ end
+ table.insert(res, state_copy)
+ return
+ end
+
+ -- 遍历所有选择
+ -- 剪枝二:从 start 开始遍历,避免生成重复子集
+ -- 剪枝三:从 start 开始遍历,避免重复选择同一元素
+ for i = start, #choices do
+ -- 剪枝一:若子集和超过 target ,则直接结束循环
+ -- 这是因为数组已排序,后边元素更大,子集和一定超过 target
+ if target - choices[i] < 0 then
+ break
+ end
+
+ -- 剪枝四:如果该元素与左边元素相等,说明该搜索分支重复,直接跳过
+ if i > start and choices[i] == choices[i - 1] then
+ -- Lua 没有 continue 关键字,改用if-else结构
+ else
+ -- 尝试:做出选择,更新 target, start
+ table.insert(state, choices[i])
+ -- 进行下一轮选择
+ backtrack(state, target - choices[i], choices, i + 1, res)
+ -- 回退:撤销选择,恢复到之前的状态
+ table.remove(state)
+ end
+ end
+end
+
+--- 求解子集和 II
+--- @param nums table 输入数字列表
+--- @param target number 目标和
+--- @return table 所有和等于目标的子集列表
+local function subset_sum_ii(nums, target)
+ local state = {} -- 状态(子集)
+ table.sort(nums) -- 对 nums 进行排序
+ local start = 1 -- 遍历起始点(Lua索引从1开始)
+ local res = {} -- 结果列表(子集列表)
+ backtrack(state, target, nums, start, res)
+ return res
+end
+
+-- Driver Code
+local function main()
+ local nums = { 4, 4, 5 }
+ local target = 9
+ local res = subset_sum_ii(nums, target)
+
+ print(string.format("输入数组 nums = [%s], target = %d", table.concat(nums, ", "), target))
+ print(string.format("所有和等于 %d 的子集 res = [", target))
+ for _, subset in ipairs(res) do
+ print(string.format(" [%s],", table.concat(subset, ", ")))
+ end
+ print("]")
+end
+
+-- 执行主函数
+main()
diff --git a/codes/lua/chapter_computational_complexity/iteration.lua b/codes/lua/chapter_computational_complexity/iteration.lua
new file mode 100644
index 0000000000..9c0fc05dcb
--- /dev/null
+++ b/codes/lua/chapter_computational_complexity/iteration.lua
@@ -0,0 +1,80 @@
+-- @script iteration.lua
+-- @date 2025-11-10
+-- @author fisheryv (yue.fisher2025@gdhfi.com)
+
+
+--- for 循环
+--- @param n integer 循环次数
+--- @return integer 求和结果
+local function for_loop(n)
+ local res = 0
+ -- 循环求和 1, 2, ..., n-1, n
+ for i = 1, n do
+ res = res + i
+ end
+ return res
+end
+
+--- while 循环
+--- @param n integer 循环次数
+--- @return integer 求和结果
+local function while_loop(n)
+ local res = 0
+ local i = 1 -- 初始化条件变量
+ -- 循环求和 1, 2, ..., n-1, n
+ while i <= n do
+ res = res + i
+ i = i + 1 -- 更新条件变量
+ end
+ return res
+end
+
+--- while 循环(两次更新)
+--- @param n integer 循环次数
+--- @return integer 求和结果
+local function while_loop_ii(n)
+ local res = 0
+ local i = 1 -- 初始化条件变量
+ -- 循环求和 1, 4, 10, ...
+ while i <= n do
+ res = res + i
+ -- 更新条件变量
+ i = i + 1
+ i = i * 2
+ end
+ return res
+end
+
+--- 双层 for 循环
+--- @param n integer 循环次数
+--- @return string 遍历结果字符串
+local function nested_for_loop(n)
+ local res = ""
+ -- 循环 i = 1, 2, ..., n-1, n
+ for i = 1, n do
+ -- 循环 j = 1, 2, ..., n-1, n
+ for j = 1, n do
+ res = res .. "(" .. i .. ", " .. j .. "), "
+ end
+ end
+ return res
+end
+
+-- Driver Code
+local function main()
+ local n = 5
+ local res = for_loop(n)
+ print(string.format("\nfor 循环的求和结果 res = %d", res))
+
+ res = while_loop(n)
+ print(string.format("\nwhile 循环的求和结果 res = %d", res))
+
+ res = while_loop_ii(n)
+ print(string.format("\nwhile 循环(两次更新)求和结果 res = %d", res))
+
+ res = nested_for_loop(n)
+ print(string.format("\n双层 for 循环的遍历结果 %s", res))
+end
+
+-- 执行主函数
+main()
diff --git a/codes/lua/chapter_computational_complexity/recursion.lua b/codes/lua/chapter_computational_complexity/recursion.lua
new file mode 100644
index 0000000000..8000cfc27e
--- /dev/null
+++ b/codes/lua/chapter_computational_complexity/recursion.lua
@@ -0,0 +1,86 @@
+-- @script recursion.lua
+-- @date 2025-11-10
+-- @author fisheryv (yue.fisher2025@gdhfi.com)
+
+
+--- 递归求和函数
+--- @param n integer 输入的数字
+--- @return integer 1+2+...+n 的结果
+local function recur(n)
+ -- 终止条件
+ if n == 1 then
+ return 1
+ end
+ -- 递:递归调用
+ local res = recur(n - 1)
+ -- 归:返回结果
+ return n + res
+end
+
+--- 使用迭代模拟递归
+--- @param n integer 输入的数字
+--- @return integer 1+2+...+n 的结果
+local function for_loop_recur(n)
+ -- 使用一个显式的栈来模拟系统调用栈
+ local stack = {}
+ local res = 0
+ -- 递:递归调用
+ for i = n, 1, -1 do
+ -- 通过"入栈操作"模拟"递"
+ table.insert(stack, i)
+ end
+ -- 归:返回结果
+ while #stack > 0 do
+ -- 通过"出栈操作"模拟"归"
+ res = res + table.remove(stack)
+ end
+ -- res = 1+2+3+...+n
+ return res
+end
+
+--- 尾递归函数
+--- @param n integer 输入的数字
+--- @param res integer 累积结果
+--- @return integer 1+2+...+n 的结果
+local function tail_recur(n, res)
+ -- 终止条件
+ if n == 0 then
+ return res
+ end
+ -- 尾递归调用
+ return tail_recur(n - 1, res + n)
+end
+
+--- 斐波那契数列:递归
+--- @param n integer 斐波那契数列的项数
+--- @return integer 第n项斐波那契数
+local function fib(n)
+ -- 终止条件 f(1) = 0, f(2) = 1
+ if n == 1 or n == 2 then
+ return n - 1
+ end
+ -- 递归调用 f(n) = f(n-1) + f(n-2)
+ local res = fib(n - 1) + fib(n - 2)
+ -- 返回结果 f(n)
+ return res
+end
+
+-- Driver Code
+local function main()
+ local n = 5
+
+ local res = recur(n)
+ print(string.format("\n递归函数的求和结果 res = %d", res))
+
+ res = for_loop_recur(n)
+ print(string.format("\n使用迭代模拟递归求和结果 res = %d", res))
+
+ res = tail_recur(n, 0)
+ print(string.format("\n尾递归函数的求和结果 res = %d", res))
+
+ res = fib(n)
+ print(string.format("\n斐波那契数列的第 %d 项为 %d", n, res))
+end
+
+-- 执行主函数
+main()
diff --git a/codes/lua/chapter_computational_complexity/space_complexity.lua b/codes/lua/chapter_computational_complexity/space_complexity.lua
new file mode 100644
index 0000000000..a303b686bf
--- /dev/null
+++ b/codes/lua/chapter_computational_complexity/space_complexity.lua
@@ -0,0 +1,131 @@
+-- @script space_complexity.lua
+-- @date 2025-11-10
+-- @author fisheryv (yue.fisher2025@gdhfi.com)
+
+-- 添加模块搜索路径
+package.path = package.path .. ";codes/lua/modules/?.lua"
+
+local list_node = require("list_node")
+local tree_node = require("tree_node")
+local print_util = require("print_util")
+local ListNode = list_node.ListNode
+local TreeNode = tree_node.TreeNode
+
+--- 占位函数
+--- @return integer 返回0
+local function _function()
+ -- 执行某些操作
+ return 0
+end
+
+--- 常数阶空间复杂度
+--- @param n integer 输入大小
+local function constant(n)
+ -- 常量、变量、对象占用 O(1) 空间
+ local a = 0
+ local nums = {}
+ for i = 1, 10000 do
+ nums[i] = 0
+ end
+ local node = ListNode.new(0)
+
+ -- 循环中的变量占用 O(1) 空间
+ for i = 1, n do
+ local c = 0
+ end
+
+ -- 循环中的函数占用 O(1) 空间
+ for i = 1, n do
+ _function()
+ end
+end
+
+--- 线性阶空间复杂度
+--- @param n integer 输入大小
+local function linear(n)
+ -- 长度为 n 的列表占用 O(n) 空间
+ local nums = {}
+ for i = 1, n do
+ nums[i] = 0
+ end
+
+ -- 长度为 n 的哈希表占用 O(n) 空间
+ local hmap = {}
+ for i = 1, n do
+ hmap[i] = tostring(i)
+ end
+end
+
+--- 线性阶空间复杂度(递归实现)
+--- @param n integer 输入大小
+local function linear_recur(n)
+ print("递归 n = " .. n)
+ if n == 1 then
+ return
+ end
+ linear_recur(n - 1)
+end
+
+--- 平方阶空间复杂度
+--- @param n integer 输入大小
+local function quadratic(n)
+ -- 二维列表占用 O(n^2) 空间
+ local num_matrix = {}
+ for i = 1, n do
+ num_matrix[i] = {}
+ for j = 1, n do
+ num_matrix[i][j] = 0
+ end
+ end
+end
+
+--- 平方阶空间复杂度(递归实现)
+--- @param n integer 输入大小
+--- @return integer 返回值
+local function quadratic_recur(n)
+ if n <= 0 then
+ return 0
+ end
+ -- 数组 nums 长度为 n, n-1, ..., 2, 1
+ local nums = {}
+ for i = 1, n do
+ nums[i] = 0
+ end
+ return quadratic_recur(n - 1)
+end
+
+--- 指数阶空间复杂度(建立满二叉树)
+--- @param n integer 树的高度
+--- @return TreeNode|nil 树的根节点
+local function build_tree(n)
+ if n == 0 then
+ return nil
+ end
+ local root = TreeNode.new(0)
+ root.left = build_tree(n - 1)
+ root.right = build_tree(n - 1)
+ return root
+end
+
+-- Driver Code
+local function main()
+ local n = 5
+
+ -- 常数阶
+ constant(n)
+
+ -- 线性阶
+ linear(n)
+ linear_recur(n)
+
+ -- 平方阶
+ quadratic(n)
+ quadratic_recur(n)
+
+ -- 指数阶
+ local root = build_tree(n)
+ print_util.print_tree(root, nil, false)
+end
+
+-- 执行主函数
+main()
diff --git a/codes/lua/chapter_computational_complexity/time_complexity.lua b/codes/lua/chapter_computational_complexity/time_complexity.lua
new file mode 100644
index 0000000000..0346844bb0
--- /dev/null
+++ b/codes/lua/chapter_computational_complexity/time_complexity.lua
@@ -0,0 +1,204 @@
+-- @script time_complexity.lua
+-- @date 2025-11-10
+-- @author fisheryv (yue.fisher2025@gdhfi.com)
+
+--- 常数阶
+--- @param n integer 数据大小
+--- @return integer 操作数量
+local function constant(n)
+ local count = 0
+ local size = 100000
+ for _ = 1, size do
+ count = count + 1
+ end
+ return count
+end
+
+--- 线性阶
+--- @param n integer 数据大小
+--- @return integer 操作数量
+local function linear(n)
+ local count = 0
+ for _ = 1, n do
+ count = count + 1
+ end
+ return count
+end
+
+--- 线性阶(遍历数组)
+--- @param nums table 数组
+--- @return integer 操作数量
+local function array_traversal(nums)
+ local count = 0
+ -- 循环次数与数组长度成正比
+ for _, num in ipairs(nums) do
+ count = count + 1
+ end
+ return count
+end
+
+--- 平方阶
+--- @param n integer 数据大小
+--- @return integer 操作数量
+local function quadratic(n)
+ local count = 0
+ -- 循环次数与数据大小 n 成平方关系
+ for i = 1, n do
+ for j = 1, n do
+ count = count + 1
+ end
+ end
+ return count
+end
+
+--- 平方阶(冒泡排序)
+--- @param nums table 数组
+--- @return integer 操作数量
+local function bubble_sort(nums)
+ local count = 0 -- 计数器
+ -- 外循环:未排序区间为 [1, i]
+ for i = #nums, 2, -1 do
+ -- 内循环:将未排序区间 [1, i] 中的最大元素交换至该区间的最右端
+ for j = 1, i - 1 do
+ if nums[j] > nums[j + 1] then
+ -- 交换 nums[j] 与 nums[j + 1]
+ local tmp = nums[j]
+ nums[j] = nums[j + 1]
+ nums[j + 1] = tmp
+ count = count + 3 -- 元素交换包含 3 个单元操作
+ end
+ end
+ end
+ return count
+end
+
+--- 指数阶(循环实现)
+--- @param n integer 数据大小
+--- @return integer 操作数量
+local function exponential(n)
+ local count = 0
+ local base = 1
+ -- 细胞每轮一分为二,形成数列 1, 2, 4, 8, ..., 2^(n-1)
+ for _ = 1, n do
+ for _ = 1, base do
+ count = count + 1
+ end
+ base = base * 2
+ end
+ -- count = 1 + 2 + 4 + 8 + .. + 2^(n-1) = 2^n - 1
+ return count
+end
+
+--- 指数阶(递归实现)
+--- @param n integer 数据大小
+--- @return integer 操作数量
+local function exp_recur(n)
+ if n == 1 then
+ return 1
+ end
+ return exp_recur(n - 1) + exp_recur(n - 1) + 1
+end
+
+--- 对数阶(循环实现)
+--- @param n integer 数据大小
+--- @return integer 操作数量
+local function logarithmic(n)
+ local count = 0
+ while n > 1 do
+ n = n / 2
+ count = count + 1
+ end
+ return count
+end
+
+--- 对数阶(递归实现)
+--- @param n integer 数据大小
+--- @return integer 操作数量
+local function log_recur(n)
+ if n <= 1 then
+ return 0
+ end
+ return log_recur(n / 2) + 1
+end
+
+--- 线性对数阶
+--- @param n integer 数据大小
+--- @return integer 操作数量
+local function linear_log_recur(n)
+ if n <= 1 then
+ return 1
+ end
+ -- 一分为二,子问题的规模减小一半
+ local count = linear_log_recur(math.floor(n / 2)) + linear_log_recur(math.floor(n / 2))
+ -- 当前子问题包含 n 个操作
+ for _ = 1, n do
+ count = count + 1
+ end
+ return count
+end
+
+--- 阶乘阶(递归实现)
+--- @param n integer 数据大小
+--- @return integer 操作数量
+local function factorial_recur(n)
+ if n == 0 then
+ return 1
+ end
+ local count = 0
+ -- 从 1 个分裂出 n 个
+ for _ = 1, n do
+ count = count + factorial_recur(n - 1)
+ end
+ return count
+end
+
+-- Driver Code
+local function main()
+ -- 可以修改 n 运行,体会一下各种复杂度的操作数量变化趋势
+ local n = 8
+ print("输入数据大小 n = " .. n)
+
+ local count = constant(n)
+ print("常数阶的操作数量 = " .. count)
+
+ count = linear(n)
+ print("线性阶的操作数量 = " .. count)
+
+ -- 创建数组 [0, 0, ..., 0]
+ local nums = {}
+ for i = 1, n do
+ nums[i] = 0
+ end
+ count = array_traversal(nums)
+ print("线性阶(遍历数组)的操作数量 = " .. count)
+
+ count = quadratic(n)
+ print("平方阶的操作数量 = " .. count)
+
+ -- 创建数组 [n, n-1, ..., 2, 1]
+ nums = {}
+ for i = 1, n do
+ nums[i] = n - i + 1
+ end
+ count = bubble_sort(nums)
+ print("平方阶(冒泡排序)的操作数量 = " .. count)
+
+ count = exponential(n)
+ print("指数阶(循环实现)的操作数量 = " .. count)
+ count = exp_recur(n)
+ print("指数阶(递归实现)的操作数量 = " .. count)
+
+ count = logarithmic(n)
+ print("对数阶(循环实现)的操作数量 = " .. count)
+ count = log_recur(n)
+ print("对数阶(递归实现)的操作数量 = " .. count)
+
+ count = linear_log_recur(n)
+ print("线性对数阶(递归实现)的操作数量 = " .. count)
+
+ count = factorial_recur(n)
+ print("阶乘阶(递归实现)的操作数量 = " .. count)
+end
+
+-- 执行主函数
+main()
diff --git a/codes/lua/chapter_computational_complexity/worst_best_time_complexity.lua b/codes/lua/chapter_computational_complexity/worst_best_time_complexity.lua
new file mode 100644
index 0000000000..90eb9a4227
--- /dev/null
+++ b/codes/lua/chapter_computational_complexity/worst_best_time_complexity.lua
@@ -0,0 +1,53 @@
+-- @script worst_best_time_complexity.lua
+-- @date 2025-11-10
+-- @author fisheryv (yue.fisher2025@gdhfi.com)
+
+
+--- 生成一个数组,元素为: 1, 2, ..., n ,顺序被打乱
+--- @param n integer 数组长度
+--- @return table 打乱后的数组
+local function random_numbers(n)
+ -- 生成数组 nums =: 1, 2, 3, ..., n
+ local nums = {}
+ for i = 1, n do
+ nums[i] = i
+ end
+
+ -- 随机打乱数组元素
+ for i = n, 2, -1 do
+ local j = math.random(i)
+ nums[i], nums[j] = nums[j], nums[i]
+ end
+
+ return nums
+end
+
+--- 查找数组 nums 中数字 1 所在索引
+--- @param nums table 待查找数组
+--- @return integer 数字1的索引,未找到返回-1
+local function find_one(nums)
+ for i = 1, #nums do
+ -- 当元素 1 在数组头部时,达到最佳时间复杂度 O(1)
+ -- 当元素 1 在数组尾部时,达到最差时间复杂度 O(n)
+ if nums[i] == 1 then
+ return i
+ end
+ end
+ return -1
+end
+
+-- Driver Code
+local function main()
+ math.randomseed(os.time()) -- 初始化随机数种子
+
+ for _ = 1, 10 do
+ local n = 100
+ local nums = random_numbers(n)
+ local index = find_one(nums)
+ print("\n数组 [ 1, 2, ..., n ] 被打乱后 = [" .. table.concat(nums, ", ") .. "]")
+ print("数字 1 的索引为 " .. index)
+ end
+end
+
+-- 执行主函数
+main()
diff --git a/codes/lua/chapter_divide_and_conquer/binary_search_recur.lua b/codes/lua/chapter_divide_and_conquer/binary_search_recur.lua
new file mode 100644
index 0000000000..42e653017e
--- /dev/null
+++ b/codes/lua/chapter_divide_and_conquer/binary_search_recur.lua
@@ -0,0 +1,55 @@
+-- @script binary_search_recur.lua
+-- @date 2025-11-15
+-- @author fisheryv (yue.fisher2025@gdhfi.com)
+
+--- 二分查找:问题 f(i, j)
+--- @param nums table 有序数组
+--- @param target integer 目标值
+--- @param i integer 区间起始索引
+--- @param j integer 区间结束索引
+--- @return integer 目标元素的索引,如果未找到则返回-1
+--- @warning Lua table 索引从 1 开始
+local function dfs(nums, target, i, j)
+ -- 若区间为空,代表无目标元素,则返回 -1
+ if i > j then
+ return -1
+ end
+
+ -- 计算中点索引 m
+ local m = math.floor((i + j) / 2)
+
+ if nums[m] < target then
+ -- 递归子问题 f(m+1, j)
+ return dfs(nums, target, m + 1, j)
+ elseif nums[m] > target then
+ -- 递归子问题 f(i, m-1)
+ return dfs(nums, target, i, m - 1)
+ else
+ -- 找到目标元素,返回其索引
+ return m
+ end
+end
+
+--- 二分查找
+--- @param nums table 有序数组
+--- @param target integer 目标值
+--- @return integer 目标元素的索引,如果未找到则返回-1
+--- @warning Lua table 索引从 1 开始
+local function search(nums, target)
+ local n = #nums
+ -- 求解问题 f(1, n)
+ return dfs(nums, target, 1, n)
+end
+
+-- Driver Code
+local function main()
+ local target = 6
+ local nums = { 1, 3, 6, 8, 12, 15, 23, 26, 31, 35 }
+
+ -- 二分查找(双闭区间)
+ local index = search(nums, target) -- 注意:Lua 索引从 1 开始
+ print("目标元素 6 的索引 = " .. tostring(index))
+end
+
+-- 执行主函数
+main()
diff --git a/codes/lua/chapter_divide_and_conquer/build_tree.lua b/codes/lua/chapter_divide_and_conquer/build_tree.lua
new file mode 100644
index 0000000000..26b30d277f
--- /dev/null
+++ b/codes/lua/chapter_divide_and_conquer/build_tree.lua
@@ -0,0 +1,70 @@
+-- @script build_tree.lua
+-- @date 2025-11-15
+-- @author fisheryv (yue.fisher2025@gdhfi.com)
+
+-- 添加模块搜索路径
+package.path = package.path .. ";codes/lua/modules/?.lua"
+
+local print_util = require("print_util")
+local tree_node = require("tree_node")
+local TreeNode = tree_node.TreeNode
+
+--- 使用分治法构建二叉树
+--- @param preorder table 前序遍历序列
+--- @param inorder_map table 中序遍历值到索引的映射
+--- @param i integer 当前根节点在前序遍历中的索引
+--- @param l integer 当前子树在中序遍历中的左边界
+--- @param r integer 当前子树在中序遍历中的右边界
+--- @return TreeNode|nil 构建的二叉树根节点
+local function dfs(preorder, inorder_map, i, l, r)
+ -- 子树区间为空时终止
+ if r < l then
+ return nil
+ end
+
+ -- 初始化根节点
+ local root = TreeNode.new(preorder[i])
+
+ -- 查询 m,从而划分左右子树
+ local m = inorder_map[preorder[i]]
+
+ -- 子问题:构建左子树
+ root.left = dfs(preorder, inorder_map, i + 1, l, m - 1)
+
+ -- 子问题:构建右子树
+ root.right = dfs(preorder, inorder_map, i + 1 + (m - l), m + 1, r)
+
+ -- 返回根节点
+ return root
+end
+
+--- 根据前序遍历和中序遍历构建二叉树
+--- @param preorder table 前序遍历序列
+--- @param inorder table 中序遍历序列
+--- @return TreeNode|nil 构建的二叉树根节点
+local function build_tree(preorder, inorder)
+ -- 初始化哈希表,存储 inorder 元素到索引的映射
+ local inorder_map = {}
+ for idx, val in ipairs(inorder) do
+ inorder_map[val] = idx
+ end
+
+ local root = dfs(preorder, inorder_map, 1, 1, #inorder)
+ return root
+end
+
+-- Driver Code
+local function main()
+ local preorder = { 3, 9, 2, 1, 7 }
+ local inorder = { 9, 3, 1, 2, 7 }
+
+ print("前序遍历 = [" .. table.concat(preorder, ", ") .. "]")
+ print("中序遍历 = [" .. table.concat(inorder, ", ") .. "]")
+
+ local root = build_tree(preorder, inorder)
+ print("构建的二叉树为:")
+ print_util.print_tree(root, nil, false)
+end
+
+-- 执行主函数
+main()
diff --git a/codes/lua/chapter_divide_and_conquer/hanota.lua b/codes/lua/chapter_divide_and_conquer/hanota.lua
new file mode 100644
index 0000000000..489932f961
--- /dev/null
+++ b/codes/lua/chapter_divide_and_conquer/hanota.lua
@@ -0,0 +1,66 @@
+-- @script hanota.lua
+-- @date 2025-11-15
+-- @author fisheryv (yue.fisher2025@gdhfi.com)
+
+--- 移动一个圆盘
+--- @param src table 源柱子
+--- @param tar table 目标柱子
+local function move(src, tar)
+ -- 从 src 顶部拿出一个圆盘
+ local pan = table.remove(src)
+ -- 将圆盘放入 tar 顶部
+ table.insert(tar, pan)
+end
+
+--- 求解汉诺塔问题 f(i)
+--- @param i number 要移动的圆盘数量
+--- @param src table 源柱子
+--- @param buf table 缓冲柱子
+--- @param tar table 目标柱子
+local function dfs(i, src, buf, tar)
+ -- 若 src 只剩下一个圆盘,则直接将其移到 tar
+ if i == 1 then
+ move(src, tar)
+ return
+ end
+
+ -- 子问题 f(i-1) :将 src 顶部 i-1 个圆盘借助 tar 移到 buf
+ dfs(i - 1, src, tar, buf)
+ -- 子问题 f(1) :将 src 剩余一个圆盘移到 tar
+ move(src, tar)
+ -- 子问题 f(i-1) :将 buf 顶部 i-1 个圆盘借助 src 移到 tar
+ dfs(i - 1, buf, src, tar)
+end
+
+--- 求解汉诺塔问题
+--- @param A table 起始柱子A
+--- @param B table 辅助柱子B
+--- @param C table 目标柱子C
+local function solve_hanota(A, B, C)
+ local n = #A
+ -- 将 A 顶部 n 个圆盘借助 B 移到 C
+ dfs(n, A, B, C)
+end
+
+-- Driver Code
+local function main()
+ -- 列表尾部是柱子顶部
+ local A = { 5, 4, 3, 2, 1 }
+ local B = {}
+ local C = {}
+
+ print("初始状态下:")
+ print("A = [" .. table.concat(A, ", ") .. "]")
+ print("B = [" .. table.concat(B, ", ") .. "]")
+ print("C = [" .. table.concat(C, ", ") .. "]")
+
+ solve_hanota(A, B, C)
+
+ print("圆盘移动完成后:")
+ print("A = [" .. table.concat(A, ", ") .. "]")
+ print("B = [" .. table.concat(B, ", ") .. "]")
+ print("C = [" .. table.concat(C, ", ") .. "]")
+end
+
+-- 执行主函数
+main()
diff --git a/codes/lua/chapter_dynamic_programming/climbing_stairs_backtrack.lua b/codes/lua/chapter_dynamic_programming/climbing_stairs_backtrack.lua
new file mode 100644
index 0000000000..bae18bccda
--- /dev/null
+++ b/codes/lua/chapter_dynamic_programming/climbing_stairs_backtrack.lua
@@ -0,0 +1,48 @@
+-- @script climbing_stairs_backtrack.lua
+-- @date 2025-11-16
+-- @author fisheryv (yue.fisher2025@gdhfi.com)
+
+--- 回溯算法
+--- @param choices table 可选择的步长列表
+--- @param state integer 当前所在阶梯
+--- @param n integer 目标阶梯数
+--- @param res table 结果计数器(使用表传递以支持修改)
+local function backtrack(choices, state, n, res)
+ -- 当爬到第 n 阶时,方案数量加 1
+ if state == n then
+ res[1] = res[1] + 1
+ return
+ end
+
+ -- 遍历所有选择
+ for _, choice in ipairs(choices) do
+ -- 剪枝:不允许越过第 n 阶
+ if state + choice <= n then
+ -- 尝试:做出选择,更新状态
+ backtrack(choices, state + choice, n, res)
+ -- 回退(在Lua中不需要显式回退,参数是按值传递的)
+ end
+ end
+end
+
+--- 爬楼梯:回溯算法
+--- @param n integer 楼梯阶数
+--- @return integer 爬楼梯的方案数量
+local function climbing_stairs_backtrack(n)
+ local choices = { 1, 2 } -- 可选择向上爬 1 阶或 2 阶
+ local state = 0 -- 从第 0 阶开始爬
+ local res = { 0 } -- 使用 res[1] 记录方案数量
+
+ backtrack(choices, state, n, res)
+ return res[1]
+end
+
+-- Driver Code
+local function main()
+ local n = 9
+ local res = climbing_stairs_backtrack(n)
+ print(string.format("爬 %d 阶楼梯共有 %d 种方案", n, res))
+end
+
+-- 执行主函数
+main()
diff --git a/codes/lua/chapter_dynamic_programming/climbing_stairs_constraint_dp.lua b/codes/lua/chapter_dynamic_programming/climbing_stairs_constraint_dp.lua
new file mode 100644
index 0000000000..06ea17f206
--- /dev/null
+++ b/codes/lua/chapter_dynamic_programming/climbing_stairs_constraint_dp.lua
@@ -0,0 +1,46 @@
+-- @script climbing_stairs_constraint_dp.lua
+-- @date 2025-11-16
+-- @author fisheryv (yue.fisher2025@gdhfi.com)
+
+--- 带约束爬楼梯:动态规划
+--- @param n integer 楼梯的阶数
+--- @return integer 爬楼梯的方案总数
+local function climbing_stairs_constraint_dp(n)
+ -- 基础情况处理
+ if n == 1 or n == 2 then
+ return 1
+ end
+
+ -- 初始化动态规划表
+ -- dp[i][1] 表示到达第i阶且最后一步是1阶的方案数
+ -- dp[i][2] 表示到达第i阶且最后一步是2阶的方案数
+ local dp = {}
+ for i = 1, n + 1 do
+ dp[i] = { 0, 0 }
+ end
+
+ -- 设置初始状态
+ dp[1][1], dp[1][2] = 1, 0 -- 第1阶:只能1步到达
+ dp[2][1], dp[2][2] = 0, 1 -- 第2阶:只能2步到达
+
+ -- 状态转移:从较小问题逐步求解较大问题
+ for i = 3, n do
+ -- 最后一步是1阶:前一步必须是2阶(不能连续两次1阶)
+ dp[i][1] = dp[i - 1][2]
+ -- 最后一步是2阶:可以从i-2阶通过1阶或2阶到达
+ dp[i][2] = dp[i - 2][1] + dp[i - 2][2]
+ end
+
+ -- 返回总方案数:到达第n阶的所有可能方式
+ return dp[n][1] + dp[n][2]
+end
+
+-- Driver Code
+local function main()
+ local n = 9
+ local res = climbing_stairs_constraint_dp(n)
+ print(string.format("爬 %d 阶楼梯共有 %d 种方案", n, res))
+end
+
+-- 执行主程序
+main()
diff --git a/codes/lua/chapter_dynamic_programming/climbing_stairs_dfs.lua b/codes/lua/chapter_dynamic_programming/climbing_stairs_dfs.lua
new file mode 100644
index 0000000000..369c128812
--- /dev/null
+++ b/codes/lua/chapter_dynamic_programming/climbing_stairs_dfs.lua
@@ -0,0 +1,34 @@
+-- @script climbing_stairs_dfs.lua
+-- @date 2025-11-16
+-- @author fisheryv (yue.fisher2025@gdhfi.com)
+
+--- 搜索
+--- @param i integer 当前台阶数
+--- @return integer 到达当前台阶的方案数
+local function dfs(i)
+ -- 已知 dp[1] 和 dp[2],直接返回
+ if i == 1 or i == 2 then
+ return i
+ end
+
+ -- dp[i] = dp[i-1] + dp[i-2]
+ local count = dfs(i - 1) + dfs(i - 2)
+ return count
+end
+
+--- 爬楼梯:搜索
+--- @param n integer 楼梯阶数
+--- @return integer 爬楼梯的方案总数
+local function climbing_stairs_dfs(n)
+ return dfs(n)
+end
+
+-- Driver Code
+local function main()
+ local n = 9
+ local res = climbing_stairs_dfs(n)
+ print(string.format("爬 %d 阶楼梯共有 %d 种方案", n, res))
+end
+
+-- 执行主函数
+main()
diff --git a/codes/lua/chapter_dynamic_programming/climbing_stairs_dfs_mem.lua b/codes/lua/chapter_dynamic_programming/climbing_stairs_dfs_mem.lua
new file mode 100644
index 0000000000..66f6b34abf
--- /dev/null
+++ b/codes/lua/chapter_dynamic_programming/climbing_stairs_dfs_mem.lua
@@ -0,0 +1,50 @@
+-- @script climbing_stairs_dfs_mem.lua
+-- @date 2025-11-16
+-- @author fisheryv (yue.fisher2025@gdhfi.com)
+
+--- 记忆化搜索
+--- @param i integer 当前楼梯阶数
+--- @param mem table 记忆化表格
+--- @return integer 爬到第 i 阶的方案总数
+local function dfs(i, mem)
+ -- 基本情况:第 1 阶有 1 种方案,第 2 阶有 2 种方案
+ if i == 1 or i == 2 then
+ return i
+ end
+
+ -- 如果已经计算过,直接返回结果
+ if mem[i] ~= -1 then
+ return mem[i]
+ end
+
+ -- 递归计算:dp[i] = dp[i-1] + dp[i-2]
+ local count = dfs(i - 1, mem) + dfs(i - 2, mem)
+
+ -- 记录结果到记忆化表格
+ mem[i] = count
+
+ return count
+end
+
+--- 爬楼梯:记忆化搜索
+--- @param n integer 楼梯总阶数
+--- @return integer 爬 n 阶楼梯的方案总数
+local function climbing_stairs_dfs_mem(n)
+ -- mem[i] 记录爬到第 i 阶的方案总数,-1 代表无记录
+ local mem = {}
+ for i = 0, n do
+ mem[i] = -1
+ end
+
+ return dfs(n, mem)
+end
+
+-- Driver Code
+local function main()
+ local n = 9
+ local res = climbing_stairs_dfs_mem(n)
+ print(string.format("爬 %d 阶楼梯共有 %d 种方案", n, res))
+end
+
+-- 执行主函数
+main()
diff --git a/codes/lua/chapter_dynamic_programming/climbing_stairs_dp.lua b/codes/lua/chapter_dynamic_programming/climbing_stairs_dp.lua
new file mode 100644
index 0000000000..859acf6923
--- /dev/null
+++ b/codes/lua/chapter_dynamic_programming/climbing_stairs_dp.lua
@@ -0,0 +1,60 @@
+-- @script climbing_stairs_dp.lua
+-- @date 2025-11-16
+-- @author fisheryv (yue.fisher2025@gdhfi.com)
+
+--- 爬楼梯:动态规划
+--- @param n integer 楼梯阶数
+--- @return integer 爬楼梯的方案数
+local function climbing_stairs_dp(n)
+ if n == 1 or n == 2 then
+ return n
+ end
+
+ -- 初始化 dp 表,用于存储子问题的解
+ local dp = {}
+ for i = 1, n + 1 do
+ dp[i] = 0
+ end
+
+ -- 初始状态:预设最小子问题的解
+ dp[1] = 1
+ dp[2] = 2
+
+ -- 状态转移:从较小子问题逐步求解较大子问题
+ for i = 3, n do
+ dp[i] = dp[i - 1] + dp[i - 2]
+ end
+
+ return dp[n]
+end
+
+--- 爬楼梯:空间优化后的动态规划
+--- @param n integer 楼梯阶数
+--- @return integer 爬楼梯的方案数
+local function climbing_stairs_dp_comp(n)
+ if n == 1 or n == 2 then
+ return n
+ end
+
+ local a, b = 1, 2
+
+ for _ = 3, n do
+ a, b = b, a + b
+ end
+
+ return b
+end
+
+-- Driver Code
+local function main()
+ local n = 9
+
+ local res = climbing_stairs_dp(n)
+ print(string.format("爬 %d 阶楼梯共有 %d 种方案", n, res))
+
+ res = climbing_stairs_dp_comp(n)
+ print(string.format("爬 %d 阶楼梯共有 %d 种方案", n, res))
+end
+
+-- 执行主函数
+main()
diff --git a/codes/lua/chapter_dynamic_programming/coin_change.lua b/codes/lua/chapter_dynamic_programming/coin_change.lua
new file mode 100644
index 0000000000..0a84a6c2ba
--- /dev/null
+++ b/codes/lua/chapter_dynamic_programming/coin_change.lua
@@ -0,0 +1,88 @@
+-- @script coin_change.lua
+-- @date 2025-11-16
+-- @author fisheryv (yue.fisher2025@gdhfi.com)
+
+--- 零钱兑换:动态规划
+--- @param coins table 可用的硬币面值列表
+--- @param amt integer 目标金额
+--- @return integer 凑齐目标金额所需的最少硬币数,如果无法凑齐则返回-1
+local function coin_change_dp(coins, amt)
+ local n = #coins
+ local MAX = amt + 1
+
+ -- 初始化 dp 表
+ local dp = {}
+ for i = 1, n + 1 do
+ dp[i] = {}
+ for j = 1, amt + 1 do
+ dp[i][j] = 0
+ end
+ end
+
+ -- 状态转移:首行首列
+ for a = 2, amt + 1 do
+ dp[1][a] = MAX
+ end
+
+ -- 状态转移:其余行和列
+ for i = 2, n + 1 do
+ for a = 2, amt + 1 do
+ if coins[i - 1] > a - 1 then
+ -- 若超过目标金额,则不选硬币 i
+ dp[i][a] = dp[i - 1][a]
+ else
+ -- 不选和选硬币 i 这两种方案的较小值
+ dp[i][a] = math.min(dp[i - 1][a], dp[i][a - coins[i - 1]] + 1)
+ end
+ end
+ end
+
+ return dp[n + 1][amt + 1] ~= MAX and dp[n + 1][amt + 1] or -1
+end
+
+--- 零钱兑换:空间优化后的动态规划
+--- @param coins table 可用的硬币面值列表
+--- @param amt integer 目标金额
+--- @return integer 凑齐目标金额所需的最少硬币数,如果无法凑齐则返回-1
+local function coin_change_dp_comp(coins, amt)
+ local n = #coins
+ local MAX = amt + 1
+
+ -- 初始化 dp 表
+ local dp = {}
+ for i = 1, amt + 1 do
+ dp[i] = MAX
+ end
+ dp[1] = 0 -- 金额0需要0个硬币
+
+ -- 状态转移
+ for i = 1, n do
+ -- 正序遍历
+ for a = 2, amt + 1 do
+ if coins[i] <= a - 1 then
+ -- 不选和选硬币 i 这两种方案的较小值
+ dp[a] = math.min(dp[a], dp[a - coins[i]] + 1)
+ end
+ -- 若超过目标金额,dp[a]保持不变(相当于不选硬币i)
+ end
+ end
+
+ return dp[amt + 1] ~= MAX and dp[amt + 1] or -1
+end
+
+-- Driver Code
+local function main()
+ local coins = { 1, 2, 5 }
+ local amt = 4
+
+ -- 动态规划
+ local res = coin_change_dp(coins, amt)
+ print(string.format("凑到目标金额所需的最少硬币数量为 %d", res))
+
+ -- 空间优化后的动态规划
+ res = coin_change_dp_comp(coins, amt)
+ print(string.format("凑到目标金额所需的最少硬币数量为 %d", res))
+end
+
+-- 执行主函数
+main()
diff --git a/codes/lua/chapter_dynamic_programming/coin_change_ii.lua b/codes/lua/chapter_dynamic_programming/coin_change_ii.lua
new file mode 100644
index 0000000000..0875267140
--- /dev/null
+++ b/codes/lua/chapter_dynamic_programming/coin_change_ii.lua
@@ -0,0 +1,89 @@
+-- @script coin_change_ii.lua
+-- @date 2025-11-16
+-- @author fisheryv (yue.fisher2025@gdhfi.com)
+
+--- 零钱兑换 II:动态规划
+--- @param coins table 可用的硬币面值列表
+--- @param amt integer 目标金额
+--- @return integer 组合数量
+local function coin_change_ii_dp(coins, amt)
+ local n = #coins
+ -- 初始化 dp 表
+ local dp = {}
+ for i = 1, n + 1 do
+ dp[i] = {}
+ for j = 1, amt + 1 do
+ dp[i][j] = 0
+ end
+ end
+
+ -- 初始化首列
+ for i = 1, n + 1 do
+ dp[i][1] = 1
+ end
+
+ -- 状态转移
+ for i = 2, n + 1 do
+ for a = 2, amt + 1 do
+ local current_coin = coins[i - 1]
+ local current_amount = a - 1 -- 转换为实际金额(索引从1开始)
+
+ if current_coin > current_amount then
+ -- 若超过目标金额,则不选硬币 i
+ dp[i][a] = dp[i - 1][a]
+ else
+ -- 不选和选硬币 i 这两种方案之和
+ dp[i][a] = dp[i - 1][a] + dp[i][a - current_coin]
+ end
+ end
+ end
+
+ return dp[n + 1][amt + 1]
+end
+
+--- 零钱兑换 II:空间优化后的动态规划
+--- @param coins table 可用的硬币面值列表
+--- @param amt integer 目标金额
+--- @return integer 组合数量
+local function coin_change_ii_dp_comp(coins, amt)
+ local n = #coins
+ -- 初始化 dp 表
+ local dp = {}
+ for i = 1, amt + 1 do
+ dp[i] = 0
+ end
+ dp[1] = 1 -- 对应金额0的情况
+
+ -- 状态转移
+ for i = 1, n do
+ for a = 2, amt + 1 do
+ local current_coin = coins[i]
+ local current_amount = a - 1 -- 转换为实际金额(索引从1开始)
+
+ if current_coin <= current_amount then
+ -- 不选和选硬币 i 这两种方案之和
+ dp[a] = dp[a] + dp[a - current_coin]
+ end
+ -- 如果硬币金额大于当前金额,dp[a]保持不变
+ end
+ end
+
+ return dp[amt + 1]
+end
+
+-- Driver Code
+local function main()
+ local coins = { 1, 2, 5 }
+ local amt = 5
+
+ -- 动态规划
+ local res = coin_change_ii_dp(coins, amt)
+ print(string.format("凑出目标金额的硬币组合数量为 %d", res))
+
+ -- 空间优化后的动态规划
+ res = coin_change_ii_dp_comp(coins, amt)
+ print(string.format("凑出目标金额的硬币组合数量为 %d", res))
+end
+
+-- 执行主程序
+main()
diff --git a/codes/lua/chapter_dynamic_programming/edit_distance.lua b/codes/lua/chapter_dynamic_programming/edit_distance.lua
new file mode 100644
index 0000000000..a6455b3472
--- /dev/null
+++ b/codes/lua/chapter_dynamic_programming/edit_distance.lua
@@ -0,0 +1,193 @@
+-- @script edit_distance.lua
+-- @date 2025-11-16
+-- @author fisheryv (yue.fisher2025@gdhfi.com)
+
+--- 编辑距离:暴力搜索
+--- @param s string 源字符串
+--- @param t string 目标字符串
+--- @param i integer 源字符串当前处理位置
+--- @param j integer 目标字符串当前处理位置
+--- @return integer 编辑距离
+local function edit_distance_dfs(s, t, i, j)
+ -- 若 s 和 t 都为空,则返回 0
+ if i == 0 and j == 0 then
+ return 0
+ end
+
+ -- 若 s 为空,则返回 t 长度
+ if i == 0 then
+ return j
+ end
+
+ -- 若 t 为空,则返回 s 长度
+ if j == 0 then
+ return i
+ end
+
+ -- 若两字符相等,则直接跳过此两字符
+ if s:sub(i, i) == t:sub(j, j) then
+ return edit_distance_dfs(s, t, i - 1, j - 1)
+ end
+
+ -- 最少编辑步数 = 插入、删除、替换这三种操作的最少编辑步数 + 1
+ local insert_cost = edit_distance_dfs(s, t, i, j - 1)
+ local delete_cost = edit_distance_dfs(s, t, i - 1, j)
+ local replace_cost = edit_distance_dfs(s, t, i - 1, j - 1)
+
+ -- 返回最少编辑步数
+ return math.min(insert_cost, delete_cost, replace_cost) + 1
+end
+
+--- 编辑距离:记忆化搜索
+--- @param s string 源字符串
+--- @param t string 目标字符串
+--- @param mem table 记忆化表格
+--- @param i integer 源字符串当前处理位置
+--- @param j integer 目标字符串当前处理位置
+--- @return integer 编辑距离
+local function edit_distance_dfs_mem(s, t, mem, i, j)
+ -- 若 s 和 t 都为空,则返回 0
+ if i == 0 and j == 0 then
+ return 0
+ end
+
+ -- 若 s 为空,则返回 t 长度
+ if i == 0 then
+ return j
+ end
+
+ -- 若 t 为空,则返回 s 长度
+ if j == 0 then
+ return i
+ end
+
+ -- 若已有记录,则直接返回之
+ if mem[i][j] ~= -1 then
+ return mem[i][j]
+ end
+
+ -- 若两字符相等,则直接跳过此两字符
+ if s:sub(i, i) == t:sub(j, j) then
+ mem[i][j] = edit_distance_dfs_mem(s, t, mem, i - 1, j - 1)
+ return mem[i][j]
+ end
+
+ -- 最少编辑步数 = 插入、删除、替换这三种操作的最少编辑步数 + 1
+ local insert_cost = edit_distance_dfs_mem(s, t, mem, i, j - 1)
+ local delete_cost = edit_distance_dfs_mem(s, t, mem, i - 1, j)
+ local replace_cost = edit_distance_dfs_mem(s, t, mem, i - 1, j - 1)
+
+ -- 记录并返回最少编辑步数
+ mem[i][j] = math.min(insert_cost, delete_cost, replace_cost) + 1
+ return mem[i][j]
+end
+
+--- 编辑距离:动态规划
+--- @param s string 源字符串
+--- @param t string 目标字符串
+--- @return integer 编辑距离
+local function edit_distance_dp(s, t)
+ local n, m = #s, #t
+ local dp = {}
+
+ -- 初始化DP表
+ for i = 0, n do
+ dp[i] = {}
+ for j = 0, m do
+ dp[i][j] = 0
+ end
+ end
+
+ -- 状态转移:首行首列
+ for i = 1, n do
+ dp[i][0] = i
+ end
+
+ for j = 1, m do
+ dp[0][j] = j
+ end
+
+ -- 状态转移:其余行和列
+ for i = 1, n do
+ for j = 1, m do
+ if s:sub(i, i) == t:sub(j, j) then
+ -- 若两字符相等,则直接跳过此两字符
+ dp[i][j] = dp[i - 1][j - 1]
+ else
+ -- 最少编辑步数 = 插入、删除、替换这三种操作的最少编辑步数 + 1
+ dp[i][j] = math.min(dp[i][j - 1], dp[i - 1][j], dp[i - 1][j - 1]) + 1
+ end
+ end
+ end
+
+ return dp[n][m]
+end
+
+--- 编辑距离:空间优化后的动态规划
+--- @param s string 源字符串
+--- @param t string 目标字符串
+--- @return integer 编辑距离
+local function edit_distance_dp_comp(s, t)
+ local n, m = #s, #t
+ local dp = {}
+
+ -- 初始化DP数组
+ for j = 0, m do
+ dp[j] = j
+ end
+
+ -- 状态转移:其余行
+ for i = 1, n do
+ -- 状态转移:首列
+ local leftup = dp[0] -- 暂存 dp[i-1, j-1]
+ dp[0] = i
+
+ -- 状态转移:其余列
+ for j = 1, m do
+ local temp = dp[j]
+ if s:sub(i, i) == t:sub(j, j) then
+ -- 若两字符相等,则直接跳过此两字符
+ dp[j] = leftup
+ else
+ -- 最少编辑步数 = 插入、删除、替换这三种操作的最少编辑步数 + 1
+ dp[j] = math.min(dp[j - 1], dp[j], leftup) + 1
+ end
+ leftup = temp -- 更新为下一轮的 dp[i-1, j-1]
+ end
+ end
+
+ return dp[m]
+end
+
+-- Driver Code
+local function main()
+ local s = "bag"
+ local t = "pack"
+ local n, m = #s, #t
+
+ -- 暴力搜索
+ local res = edit_distance_dfs(s, t, n, m)
+ print(string.format("将 %s 更改为 %s 最少需要编辑 %d 步", s, t, res))
+
+ -- 记忆化搜索
+ local mem = {}
+ for i = 0, n do
+ mem[i] = {}
+ for j = 0, m do
+ mem[i][j] = -1
+ end
+ end
+ res = edit_distance_dfs_mem(s, t, mem, n, m)
+ print(string.format("将 %s 更改为 %s 最少需要编辑 %d 步", s, t, res))
+
+ -- 动态规划
+ res = edit_distance_dp(s, t)
+ print(string.format("将 %s 更改为 %s 最少需要编辑 %d 步", s, t, res))
+
+ -- 空间优化后的动态规划
+ res = edit_distance_dp_comp(s, t)
+ print(string.format("将 %s 更改为 %s 最少需要编辑 %d 步", s, t, res))
+end
+
+-- 执行主函数
+main()
diff --git a/codes/lua/chapter_dynamic_programming/knapsack.lua b/codes/lua/chapter_dynamic_programming/knapsack.lua
new file mode 100644
index 0000000000..b000d3ab78
--- /dev/null
+++ b/codes/lua/chapter_dynamic_programming/knapsack.lua
@@ -0,0 +1,154 @@
+-- @script knapsack.lua
+-- @date 2025-11-16
+-- @author fisheryv (yue.fisher2025@gdhfi.com)
+
+--- 0-1 背包:暴力搜索
+--- @param wgt table 物品重量列表
+--- @param val table 物品价值列表
+--- @param i integer 当前考虑的物品索引
+--- @param c integer 背包剩余容量
+--- @return integer 最大价值
+local function knapsack_dfs(wgt, val, i, c)
+ -- 若已选完所有物品或背包无剩余容量,则返回价值 0
+ if i == 0 or c == 0 then
+ return 0
+ end
+
+ -- 若超过背包容量,则只能选择不放入背包
+ if wgt[i] > c then
+ return knapsack_dfs(wgt, val, i - 1, c)
+ end
+
+ -- 计算不放入和放入物品 i 的最大价值
+ local no = knapsack_dfs(wgt, val, i - 1, c)
+ local yes = knapsack_dfs(wgt, val, i - 1, c - wgt[i]) + val[i]
+
+ -- 返回两种方案中价值更大的那一个
+ return math.max(no, yes)
+end
+
+--- 0-1 背包:记忆化搜索
+--- @param wgt table 物品重量列表
+--- @param val table 物品价值列表
+--- @param mem table 记忆化表格
+--- @param i integer 当前考虑的物品索引
+--- @param c integer 背包剩余容量
+--- @return integer 最大价值
+local function knapsack_dfs_mem(wgt, val, mem, i, c)
+ -- 若已选完所有物品或背包无剩余容量,则返回价值 0
+ if i == 0 or c == 0 then
+ return 0
+ end
+
+ -- 若已有记录,则直接返回
+ if mem[i][c] ~= -1 then
+ return mem[i][c]
+ end
+
+ -- 若超过背包容量,则只能选择不放入背包
+ if wgt[i] > c then
+ mem[i][c] = knapsack_dfs_mem(wgt, val, mem, i - 1, c)
+ return mem[i][c]
+ end
+
+ -- 计算不放入和放入物品 i 的最大价值
+ local no = knapsack_dfs_mem(wgt, val, mem, i - 1, c)
+ local yes = knapsack_dfs_mem(wgt, val, mem, i - 1, c - wgt[i]) + val[i]
+
+ -- 记录并返回两种方案中价值更大的那一个
+ mem[i][c] = math.max(no, yes)
+ return mem[i][c]
+end
+
+--- 0-1 背包:动态规划
+--- @param wgt table 物品重量列表
+--- @param val table 物品价值列表
+--- @param cap integer 背包容量
+--- @return integer 最大价值
+local function knapsack_dp(wgt, val, cap)
+ local n = #wgt
+ -- 初始化 dp 表
+ local dp = {}
+ for i = 0, n do
+ dp[i] = {}
+ for j = 0, cap do
+ dp[i][j] = 0
+ end
+ end
+
+ -- 状态转移
+ for i = 1, n do
+ for c = 1, cap do
+ if wgt[i] > c then
+ -- 若超过背包容量,则不选物品 i
+ dp[i][c] = dp[i - 1][c]
+ else
+ -- 不选和选物品 i 这两种方案的较大值
+ dp[i][c] = math.max(dp[i - 1][c], dp[i - 1][c - wgt[i]] + val[i])
+ end
+ end
+ end
+
+ return dp[n][cap]
+end
+
+--- 0-1 背包:空间优化后的动态规划
+--- @param wgt table 物品重量列表
+--- @param val table 物品价值列表
+--- @param cap integer 背包容量
+--- @return integer 最大价值
+local function knapsack_dp_comp(wgt, val, cap)
+ local n = #wgt
+ -- 初始化 dp 表
+ local dp = {}
+ for i = 0, cap do
+ dp[i] = 0
+ end
+
+ -- 状态转移
+ for i = 1, n do
+ -- 倒序遍历
+ for c = cap, 1, -1 do
+ if wgt[i] <= c then
+ -- 不选和选物品 i 这两种方案的较大值
+ dp[c] = math.max(dp[c], dp[c - wgt[i]] + val[i])
+ end
+ end
+ end
+
+ return dp[cap]
+end
+
+-- Driver Code
+local function main()
+ local wgt = { 10, 20, 30, 40, 50 }
+ local val = { 50, 120, 150, 210, 240 }
+ local cap = 50
+ local n = #wgt
+
+ -- 暴力搜索
+ local res = knapsack_dfs(wgt, val, n, cap)
+ print(string.format("不超过背包容量的最大物品价值为 %d", res))
+
+ -- 记忆化搜索
+ local mem = {}
+ for i = 0, n do
+ mem[i] = {}
+ for j = 0, cap do
+ mem[i][j] = -1
+ end
+ end
+ res = knapsack_dfs_mem(wgt, val, mem, n, cap)
+ print(string.format("不超过背包容量的最大物品价值为 %d", res))
+
+ -- 动态规划
+ res = knapsack_dp(wgt, val, cap)
+ print(string.format("不超过背包容量的最大物品价值为 %d", res))
+
+ -- 空间优化后的动态规划
+ res = knapsack_dp_comp(wgt, val, cap)
+ print(string.format("不超过背包容量的最大物品价值为 %d", res))
+end
+
+-- 执行测试代码
+main()
diff --git a/codes/lua/chapter_dynamic_programming/min_cost_climbing_stairs_dp.lua b/codes/lua/chapter_dynamic_programming/min_cost_climbing_stairs_dp.lua
new file mode 100644
index 0000000000..12b15bc15d
--- /dev/null
+++ b/codes/lua/chapter_dynamic_programming/min_cost_climbing_stairs_dp.lua
@@ -0,0 +1,63 @@
+-- @script min_cost_climbing_stairs_dp.lua
+-- @date 2025-11-16
+-- @author fisheryv (yue.fisher2025@gdhfi.com)
+
+--- 爬楼梯最小代价:动态规划
+--- @param cost table 楼梯代价列表,第一个元素为起始位置代价
+--- @return integer 最小代价
+local function min_cost_climbing_stairs_dp(cost)
+ local n = #cost
+ if n == 1 or n == 2 then
+ return cost[n]
+ end
+
+ -- 初始化 dp 表,用于存储子问题的解
+ local dp = {}
+ for i = 1, n do
+ dp[i] = 0
+ end
+
+ -- 初始状态:预设最小子问题的解
+ dp[1] = cost[1]
+ dp[2] = cost[2]
+
+ -- 状态转移:从较小子问题逐步求解较大子问题
+ for i = 3, n do
+ dp[i] = math.min(dp[i - 1], dp[i - 2]) + cost[i]
+ end
+
+ return dp[n]
+end
+
+--- 爬楼梯最小代价:空间优化后的动态规划
+--- @param cost table 楼梯代价列表,第一个元素为起始位置代价
+--- @return integer 最小代价
+local function min_cost_climbing_stairs_dp_comp(cost)
+ local n = #cost
+ if n == 1 or n == 2 then
+ return cost[n]
+ end
+
+ local a, b = cost[1], cost[2]
+
+ for i = 3, n do
+ a, b = b, math.min(a, b) + cost[i]
+ end
+
+ return b
+end
+
+-- Driver Code
+local function main()
+ local cost = { 0, 1, 10, 1, 1, 1, 10, 1, 1, 10, 1 }
+ print(string.format("输入楼梯的代价列表为 [%s]", table.concat(cost, ", ")))
+
+ local res = min_cost_climbing_stairs_dp(cost)
+ print(string.format("爬完楼梯的最低代价为 %d", res))
+
+ res = min_cost_climbing_stairs_dp_comp(cost)
+ print(string.format("爬完楼梯的最低代价为 %d", res))
+end
+
+-- 执行主函数
+main()
diff --git a/codes/lua/chapter_dynamic_programming/min_path_sum.lua b/codes/lua/chapter_dynamic_programming/min_path_sum.lua
new file mode 100644
index 0000000000..ab724b12b6
--- /dev/null
+++ b/codes/lua/chapter_dynamic_programming/min_path_sum.lua
@@ -0,0 +1,147 @@
+-- @script min_path_sum.lua
+-- @date 2025-11-16
+-- @author fisheryv (yue.fisher2025@gdhfi.com)
+
+
+--- 最小路径和:暴力搜索
+--- @param grid table 二维网格
+--- @param i integer 当前行索引
+--- @param j integer 当前列索引
+--- @return integer 最小路径和
+local function min_path_sum_dfs(grid, i, j)
+ -- 若为左上角单元格,则终止搜索
+ if i == 0 and j == 0 then
+ return grid[1][1] -- Lua数组索引从1开始
+ end
+ -- 若行列索引越界,则返回 +∞ 代价
+ if i < 0 or j < 0 then
+ return math.huge
+ end
+ -- 计算从左上角到 (i-1, j) 和 (i, j-1) 的最小路径代价
+ local up = min_path_sum_dfs(grid, i - 1, j)
+ local left = min_path_sum_dfs(grid, i, j - 1)
+ -- 返回从左上角到 (i, j) 的最小路径代价
+ return math.min(left, up) + grid[i + 1][j + 1] -- 调整索引
+end
+
+--- 最小路径和:记忆化搜索
+--- @param grid table 二维网格
+--- @param mem table 记忆化数组
+--- @param i integer 当前行索引
+--- @param j integer 当前列索引
+--- @return integer 最小路径和
+local function min_path_sum_dfs_mem(grid, mem, i, j)
+ -- 若为左上角单元格,则终止搜索
+ if i == 0 and j == 0 then
+ return grid[1][1]
+ end
+ -- 若行列索引越界,则返回 +∞ 代价
+ if i < 0 or j < 0 then
+ return math.huge
+ end
+ -- 若已有记录,则直接返回
+ if mem[i + 1][j + 1] ~= -1 then
+ return mem[i + 1][j + 1]
+ end
+ -- 左边和上边单元格的最小路径代价
+ local up = min_path_sum_dfs_mem(grid, mem, i - 1, j)
+ local left = min_path_sum_dfs_mem(grid, mem, i, j - 1)
+ -- 记录并返回左上角到 (i, j) 的最小路径代价
+ mem[i + 1][j + 1] = math.min(left, up) + grid[i + 1][j + 1]
+ return mem[i + 1][j + 1]
+end
+
+--- 最小路径和:动态规划
+--- @param grid table 二维网格
+--- @return integer 最小路径和
+local function min_path_sum_dp(grid)
+ local n, m = #grid, #grid[1]
+ -- 初始化 dp 表
+ local dp = {}
+ for i = 1, n do
+ dp[i] = {}
+ for j = 1, m do
+ dp[i][j] = 0
+ end
+ end
+ dp[1][1] = grid[1][1]
+ -- 状态转移:首行
+ for j = 2, m do
+ dp[1][j] = dp[1][j - 1] + grid[1][j]
+ end
+ -- 状态转移:首列
+ for i = 2, n do
+ dp[i][1] = dp[i - 1][1] + grid[i][1]
+ end
+ -- 状态转移:其余行和列
+ for i = 2, n do
+ for j = 2, m do
+ dp[i][j] = math.min(dp[i][j - 1], dp[i - 1][j]) + grid[i][j]
+ end
+ end
+ return dp[n][m]
+end
+
+--- 最小路径和:空间优化后的动态规划
+--- @param grid table 二维网格
+--- @return integer 最小路径和
+local function min_path_sum_dp_comp(grid)
+ local n, m = #grid, #grid[1]
+ -- 初始化 dp 表
+ local dp = {}
+ for j = 1, m do
+ dp[j] = 0
+ end
+ -- 状态转移:首行
+ dp[1] = grid[1][1]
+ for j = 2, m do
+ dp[j] = dp[j - 1] + grid[1][j]
+ end
+ -- 状态转移:其余行
+ for i = 2, n do
+ -- 状态转移:首列
+ dp[1] = dp[1] + grid[i][1]
+ -- 状态转移:其余列
+ for j = 2, m do
+ dp[j] = math.min(dp[j - 1], dp[j]) + grid[i][j]
+ end
+ end
+ return dp[m]
+end
+
+-- Driver Code
+local function main()
+ local grid = {
+ { 1, 3, 1, 5 },
+ { 2, 2, 4, 2 },
+ { 5, 3, 2, 1 },
+ { 4, 3, 5, 2 }
+ }
+ local n, m = #grid, #grid[1]
+
+ -- 暴力搜索
+ local res = min_path_sum_dfs(grid, n - 1, m - 1)
+ print(string.format("从左上角到右下角的最小路径和为 %d", res))
+
+ -- 记忆化搜索
+ local mem = {}
+ for i = 1, n do
+ mem[i] = {}
+ for j = 1, m do
+ mem[i][j] = -1
+ end
+ end
+ res = min_path_sum_dfs_mem(grid, mem, n - 1, m - 1)
+ print(string.format("从左上角到右下角的最小路径和为 %d", res))
+
+ -- 动态规划
+ res = min_path_sum_dp(grid)
+ print(string.format("从左上角到右下角的最小路径和为 %d", res))
+
+ -- 空间优化后的动态规划
+ res = min_path_sum_dp_comp(grid)
+ print(string.format("从左上角到右下角的最小路径和为 %d", res))
+end
+
+-- 执行主函数
+main()
diff --git a/codes/lua/chapter_dynamic_programming/unbounded_knapsack.lua b/codes/lua/chapter_dynamic_programming/unbounded_knapsack.lua
new file mode 100644
index 0000000000..8cd1c52b97
--- /dev/null
+++ b/codes/lua/chapter_dynamic_programming/unbounded_knapsack.lua
@@ -0,0 +1,88 @@
+-- @script unbounded_knapsack.lua
+-- @date 2025-11-16
+-- @author fisheryv (yue.fisher2025@gdhfi.com)
+
+
+--- 完全背包:动态规划
+--- @param wgt table 物品重量列表
+--- @param val table 物品价值列表
+--- @param cap number 背包容量
+--- @return number 最大价值
+local function unbounded_knapsack_dp(wgt, val, cap)
+ local n = #wgt
+ -- 初始化 dp 表
+ local dp = {}
+ for i = 1, n + 1 do
+ dp[i] = {}
+ for j = 1, cap + 1 do
+ dp[i][j] = 0
+ end
+ end
+
+ -- 状态转移
+ for i = 1, n do
+ for c = 1, cap do
+ if wgt[i] > c then
+ -- 若超过背包容量,则不选物品 i
+ dp[i + 1][c + 1] = dp[i][c + 1]
+ else
+ -- 不选和选物品 i 这两种方案的较大值
+ dp[i + 1][c + 1] = math.max(
+ dp[i][c + 1],
+ dp[i + 1][c + 1 - wgt[i]] + val[i]
+ )
+ end
+ end
+ end
+
+ return dp[n + 1][cap + 1]
+end
+
+--- 完全背包:空间优化后的动态规划
+--- @param wgt table 物品重量列表
+--- @param val table 物品价值列表
+--- @param cap number 背包容量
+--- @return number 最大价值
+local function unbounded_knapsack_dp_comp(wgt, val, cap)
+ local n = #wgt
+ -- 初始化 dp 表
+ local dp = {}
+ for i = 1, cap + 1 do
+ dp[i] = 0
+ end
+
+ -- 状态转移
+ for i = 1, n do
+ -- 正序遍历
+ for c = 1, cap do
+ if wgt[i] <= c then
+ -- 不选和选物品 i 这两种方案的较大值
+ dp[c + 1] = math.max(
+ dp[c + 1],
+ dp[c + 1 - wgt[i]] + val[i]
+ )
+ end
+ -- 如果超过背包容量,dp[c]保持不变,无需额外操作
+ end
+ end
+
+ return dp[cap + 1]
+end
+
+-- Driver Code
+local function main()
+ local wgt = { 1, 2, 3 }
+ local val = { 5, 11, 15 }
+ local cap = 4
+
+ -- 动态规划
+ local res = unbounded_knapsack_dp(wgt, val, cap)
+ print(string.format("不超过背包容量的最大物品价值为 %d", res))
+
+ -- 空间优化后的动态规划
+ res = unbounded_knapsack_dp_comp(wgt, val, cap)
+ print(string.format("不超过背包容量的最大物品价值为 %d", res))
+end
+
+-- 执行主函数
+main()
diff --git a/codes/lua/chapter_graph/graph_adjacency_list.lua b/codes/lua/chapter_graph/graph_adjacency_list.lua
new file mode 100644
index 0000000000..f05f8f08eb
--- /dev/null
+++ b/codes/lua/chapter_graph/graph_adjacency_list.lua
@@ -0,0 +1,173 @@
+-- @script graph_adjacency_list.lua
+-- @date 2025-11-15
+-- @author fisheryv (yue.fisher2025@gdhfi.com)
+
+-- 添加模块搜索路径
+package.path = package.path .. ";codes/lua/modules/?.lua"
+
+local vertex = require("vertex")
+local Vertex = vertex.Vertex
+local vals_to_vets = vertex.vals_to_vets
+
+--- @class GraphAdjList
+--- 基于邻接表实现的无向图类
+--- @field adj_list table 邻接表
+local GraphAdjList = {}
+GraphAdjList.__index = GraphAdjList
+
+--- 构造方法
+--- @param edges table 边列表,每一条边包含两个顶点
+--- @return GraphAdjList 实例
+function GraphAdjList.new(edges)
+ local obj = setmetatable({}, GraphAdjList)
+
+ -- 邻接表,key:顶点,value:该顶点的所有邻接顶点
+ obj.adj_list = {}
+
+ -- 添加所有顶点和边
+ for _, edge in ipairs(edges) do
+ obj:add_vertex(edge[1])
+ obj:add_vertex(edge[2])
+ obj:add_edge(edge[1], edge[2])
+ end
+
+ return obj
+end
+
+--- 获取顶点数量
+--- @return integer 顶点数量
+function GraphAdjList:size()
+ local count = 0
+ for _ in pairs(self.adj_list) do
+ count = count + 1
+ end
+ return count
+end
+
+--- 添加边
+--- @param vet1 Vertex 顶点1
+--- @param vet2 Vertex 顶点2
+--- @raise 当顶点不存在或顶点相同时抛出错误
+function GraphAdjList:add_edge(vet1, vet2)
+ if not self.adj_list[vet1] or not self.adj_list[vet2] or vet1 == vet2 then
+ error("无效的边")
+ end
+
+ -- 添加边 vet1 - vet2
+ table.insert(self.adj_list[vet1], vet2)
+ table.insert(self.adj_list[vet2], vet1)
+end
+
+--- 删除边
+--- @param vet1 Vertex 顶点1
+--- @param vet2 Vertex 顶点2
+--- @raise 当顶点不存在或顶点相同时抛出错误
+function GraphAdjList:remove_edge(vet1, vet2)
+ if not self.adj_list[vet1] or not self.adj_list[vet2] or vet1 == vet2 then
+ error("无效的边")
+ end
+
+ -- 删除边 vet1 - vet2
+ self:_remove_from_list(self.adj_list[vet1], vet2)
+ self:_remove_from_list(self.adj_list[vet2], vet1)
+end
+
+--- 添加顶点
+--- @param vet Vertex 要添加的顶点
+function GraphAdjList:add_vertex(vet)
+ if self.adj_list[vet] then
+ return
+ end
+
+ -- 在邻接表中添加一个新链表
+ self.adj_list[vet] = {}
+end
+
+--- 删除顶点
+--- @param vet Vertex 要删除的顶点
+--- @raise 当顶点不存在时抛出错误
+function GraphAdjList:remove_vertex(vet)
+ if not self.adj_list[vet] then
+ error("顶点不存在")
+ end
+
+ -- 在邻接表中删除顶点 vet 对应的链表
+ self.adj_list[vet] = nil
+
+ -- 遍历其他顶点的链表,删除所有包含 vet 的边
+ for vertex, adj_vertices in pairs(self.adj_list) do
+ self:_remove_from_list(adj_vertices, vet)
+ end
+end
+
+--- 打印邻接表
+function GraphAdjList:print()
+ print("邻接表 =")
+ for vertex, adj_vertices in pairs(self.adj_list) do
+ local tmp = {}
+ for _, v in ipairs(adj_vertices) do
+ table.insert(tmp, v.val)
+ end
+ print(string.format("%d: [%s],", vertex.val, table.concat(tmp, ", ")))
+ end
+end
+
+-- 私有方法:从列表中删除指定元素
+function GraphAdjList:_remove_from_list(list, item)
+ for i = #list, 1, -1 do
+ if list[i] == item then
+ table.remove(list, i)
+ break
+ end
+ end
+end
+
+-- Driver Code
+local function main()
+ -- 初始化无向图
+ local v = vals_to_vets({ 1, 3, 2, 5, 4 })
+ local edges = {
+ { v[1], v[2] },
+ { v[1], v[4] },
+ { v[2], v[3] },
+ { v[3], v[4] },
+ { v[3], v[5] },
+ { v[4], v[5] },
+ }
+ local graph = GraphAdjList.new(edges)
+ print("\n初始化后,图为")
+ graph:print()
+
+ -- 添加边
+ -- 顶点 1, 2 即 v[1], v[3]
+ graph:add_edge(v[1], v[3])
+ print("\n添加边 1-2 后,图为")
+ graph:print()
+
+ -- 删除边
+ -- 顶点 1, 3 即 v[1], v[2]
+ graph:remove_edge(v[1], v[2])
+ print("\n删除边 1-3 后,图为")
+ graph:print()
+
+ -- 添加顶点
+ local v5 = Vertex.new(6)
+ graph:add_vertex(v5)
+ print("\n添加顶点 6 后,图为")
+ graph:print()
+
+ -- 删除顶点
+ -- 顶点 3 即 v[2]
+ graph:remove_vertex(v[2])
+ print("\n删除顶点 3 后,图为")
+ graph:print()
+end
+
+
+if ... then
+ -- 被 require 作为模块加载
+ return GraphAdjList
+else
+ -- 执行主函数
+ main()
+end
diff --git a/codes/lua/chapter_graph/graph_adjacency_matrix.lua b/codes/lua/chapter_graph/graph_adjacency_matrix.lua
new file mode 100644
index 0000000000..f87153fd3c
--- /dev/null
+++ b/codes/lua/chapter_graph/graph_adjacency_matrix.lua
@@ -0,0 +1,161 @@
+-- @script graph_adjacency_matrix.lua
+-- @date 2025-11-15
+-- @author fisheryv (yue.fisher2025@gdhfi.com)
+
+-- 添加模块搜索路径
+package.path = package.path .. ";codes/lua/modules/?.lua"
+
+local print_util = require("print_util")
+
+--- @class GraphAdjMat
+--- 基于邻接矩阵实现的无向图类
+--- @field vertices table 顶点列表,元素代表"顶点值"
+--- @field adj_mat table 邻接矩阵
+local GraphAdjMat = {}
+GraphAdjMat.__index = GraphAdjMat
+
+--- 构造方法
+--- @param vertices table 顶点列表,元素代表"顶点值"
+--- @param edges table 边列表,元素代表顶点索引,即对应vertices元素索引
+--- @return GraphAdjMat
+function GraphAdjMat.new(vertices, edges)
+ local obj = setmetatable({}, GraphAdjMat)
+
+ -- 顶点列表,元素代表"顶点值",索引代表"顶点索引"
+ obj.vertices = {}
+ -- 邻接矩阵,行列索引对应"顶点索引"
+ obj.adj_mat = {}
+
+ -- 添加顶点
+ for _, val in ipairs(vertices) do
+ obj:add_vertex(val)
+ end
+
+ -- 添加边
+ for _, e in ipairs(edges) do
+ obj:add_edge(e[1], e[2])
+ end
+
+ return obj
+end
+
+--- 获取顶点数量
+--- @return integer
+function GraphAdjMat:size()
+ return #self.vertices
+end
+
+--- 添加顶点
+--- @param val number 顶点值
+function GraphAdjMat:add_vertex(val)
+ local n = self:size()
+
+ -- 向顶点列表中添加新顶点的值
+ table.insert(self.vertices, val)
+
+ -- 在邻接矩阵中添加一行
+ local new_row = {}
+ for i = 1, n do
+ new_row[i] = 0
+ end
+ table.insert(self.adj_mat, new_row)
+
+ -- 在邻接矩阵中添加一列
+ for _, row in ipairs(self.adj_mat) do
+ row[n + 1] = 0
+ end
+end
+
+--- 删除顶点
+--- @param index integer 顶点索引
+function GraphAdjMat:remove_vertex(index)
+ if index > self:size() or index < 1 then
+ error("Index out of range")
+ end
+
+ -- 在顶点列表中移除索引index的顶点
+ table.remove(self.vertices, index)
+
+ -- 在邻接矩阵中删除索引index的行
+ table.remove(self.adj_mat, index)
+
+ -- 在邻接矩阵中删除索引index的列
+ for _, row in ipairs(self.adj_mat) do
+ table.remove(row, index)
+ end
+end
+
+--- 添加边
+--- @param i integer 顶点i的索引,对应vertices元素索引
+--- @param j integer 顶点j的索引,对应vertices元素索引
+function GraphAdjMat:add_edge(i, j)
+ -- 参数i, j对应vertices元素索引
+ -- 索引越界与相等处理
+ local n = self:size()
+ if i < 1 or j < 1 or i > n or j > n or i == j then
+ error("Index out of range or i equals j")
+ end
+
+ -- 在无向图中,邻接矩阵关于主对角线对称,即满足(i, j) == (j, i)
+ self.adj_mat[i][j] = 1
+ self.adj_mat[j][i] = 1
+end
+
+--- 删除边
+--- @param i integer 顶点i的索引,对应vertices元素索引
+--- @param j integer 顶点j的索引,对应vertices元素索引
+function GraphAdjMat:remove_edge(i, j)
+ -- 参数i, j对应vertices元素索引
+ -- 索引越界与相等处理
+ local n = self:size()
+ if i < 1 or j < 1 or i > n or j > n or i == j then
+ error("Index out of range or i equals j")
+ end
+
+ self.adj_mat[i][j] = 0
+ self.adj_mat[j][i] = 0
+end
+
+--- 打印邻接矩阵
+function GraphAdjMat:print()
+ print("顶点列表 = [" .. table.concat(self.vertices, ", ") .. "]")
+ print("邻接矩阵 =")
+ print_util.print_matrix(self.adj_mat)
+end
+
+-- Driver Code
+local function main()
+ -- 初始化无向图
+ -- 请注意,edges元素代表顶点索引,即对应vertices元素索引
+ local vertices = { 1, 3, 2, 5, 4 }
+ local edges = { { 1, 2 }, { 1, 4 }, { 2, 3 }, { 3, 4 }, { 3, 5 }, { 4, 5 } }
+ local graph = GraphAdjMat.new(vertices, edges)
+ print("\n初始化后,图为")
+ graph:print()
+
+ -- 添加边
+ -- 顶点1, 2的索引分别为1, 3
+ graph:add_edge(1, 3)
+ print("\n添加边 1-2 后,图为")
+ graph:print()
+
+ -- 删除边
+ -- 顶点1, 3的索引分别为1, 2
+ graph:remove_edge(1, 2)
+ print("\n删除边 1-3 后,图为")
+ graph:print()
+
+ -- 添加顶点
+ graph:add_vertex(6)
+ print("\n添加顶点 6 后,图为")
+ graph:print()
+
+ -- 删除顶点
+ -- 顶点3的索引为2
+ graph:remove_vertex(2)
+ print("\n删除顶点 3 后,图为")
+ graph:print()
+end
+
+-- 执行主函数
+main()
diff --git a/codes/lua/chapter_graph/graph_bfs.lua b/codes/lua/chapter_graph/graph_bfs.lua
new file mode 100644
index 0000000000..2af7a3bfca
--- /dev/null
+++ b/codes/lua/chapter_graph/graph_bfs.lua
@@ -0,0 +1,72 @@
+-- @script graph_bfs.lua
+-- @date 2025-11-15
+-- @author fisheryv (yue.fisher2025@gdhfi.com)
+
+-- 添加模块搜索路径
+package.path = package.path .. ";codes/lua/modules/?.lua"
+package.path = package.path .. ";codes/lua/chapter_graph/graph_adjacency_list.lua"
+
+local GraphAdjList = require("graph_adjacency_list")
+local vertex = require("vertex")
+local vals_to_vets = vertex.vals_to_vets
+local vets_to_vals = vertex.vets_to_vals
+
+--- 广度优先遍历
+--- @param graph GraphAdjList 图
+--- @param start_vet table 起始顶点
+--- @return table 顶点遍历序列
+local function graph_bfs(graph, start_vet)
+ -- 使用邻接表来表示图,以便获取指定顶点的所有邻接顶点
+ -- 顶点遍历序列
+ local res = {}
+ -- 哈希集合,用于记录已被访问过的顶点
+ local visited = {}
+ visited[start_vet] = true
+ -- 队列用于实现 BFS
+ local que = { start_vet }
+ -- 以顶点 vet 为起点,循环直至访问完所有顶点
+ while #que > 0 do
+ local vet = table.remove(que, 1) -- 队首顶点出队
+ table.insert(res, vet) -- 记录访问顶点
+ -- 遍历该顶点的所有邻接顶点
+ for _, adj_vet in ipairs(graph.adj_list[vet]) do
+ if not visited[adj_vet] then
+ table.insert(que, adj_vet) -- 只入队未访问的顶点
+ visited[adj_vet] = true -- 标记该顶点已被访问
+ end
+ end
+ end
+ -- 返回顶点遍历序列
+ return res
+end
+
+-- Driver Code
+local function main()
+ -- 初始化无向图
+ local v = vals_to_vets({ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 })
+ local edges = {
+ { v[1], v[2] },
+ { v[1], v[4] },
+ { v[2], v[3] },
+ { v[2], v[5] },
+ { v[3], v[6] },
+ { v[4], v[5] },
+ { v[4], v[7] },
+ { v[5], v[6] },
+ { v[5], v[8] },
+ { v[6], v[9] },
+ { v[7], v[8] },
+ { v[8], v[9] },
+ }
+ local graph = GraphAdjList.new(edges)
+ print("\n初始化后,图为")
+ graph:print()
+
+ -- 广度优先遍历
+ local res = graph_bfs(graph, v[1])
+ print("\n广度优先遍历(BFS)顶点序列为")
+ print("[" .. table.concat(vets_to_vals(res), ", ") .. "]")
+end
+
+-- 执行主函数
+main()
diff --git a/codes/lua/chapter_graph/graph_dfs.lua b/codes/lua/chapter_graph/graph_dfs.lua
new file mode 100644
index 0000000000..7e971a8eb0
--- /dev/null
+++ b/codes/lua/chapter_graph/graph_dfs.lua
@@ -0,0 +1,70 @@
+-- @script graph_dfs.lua
+-- @date 2025-11-15
+-- @author fisheryv (yue.fisher2025@gdhfi.com)
+
+-- 添加模块搜索路径
+package.path = package.path .. ";codes/lua/modules/?.lua"
+package.path = package.path .. ";codes/lua/chapter_graph/graph_adjacency_list.lua"
+
+local GraphAdjList = require("graph_adjacency_list")
+local vertex = require("vertex")
+local vets_to_vals = vertex.vets_to_vals
+local vals_to_vets = vertex.vals_to_vets
+
+--- 深度优先遍历辅助函数
+--- @param graph GraphAdjList 图
+--- @param visited table 已访问顶点集合
+--- @param res table 顶点遍历序列
+--- @param vet Vertex 当前顶点
+local function dfs(graph, visited, res, vet)
+ table.insert(res, vet) -- 记录访问顶点
+ visited[vet] = true -- 标记该顶点已被访问
+
+ -- 遍历该顶点的所有邻接顶点
+ for _, adjVet in ipairs(graph.adj_list[vet] or {}) do
+ if not visited[adjVet] then
+ -- 递归访问邻接顶点
+ dfs(graph, visited, res, adjVet)
+ end
+ end
+end
+
+--- 深度优先遍历
+--- @param graph GraphAdjList 图
+--- @param start_vet table 起始顶点
+--- @return table 顶点遍历序列
+local function dfs_traversal(graph, start_vet)
+ -- 顶点遍历序列
+ local res = {}
+ -- 哈希表,用于记录已被访问过的顶点
+ local visited = {}
+
+ dfs(graph, visited, res, start_vet)
+ return res
+end
+
+-- Driver Code
+local function main()
+ -- 初始化无向图
+ local v = vals_to_vets({ 0, 1, 2, 3, 4, 5, 6 })
+ local edges = {
+ { v[1], v[2] },
+ { v[1], v[4] },
+ { v[2], v[3] },
+ { v[3], v[6] },
+ { v[5], v[6] },
+ { v[6], v[7] },
+ }
+
+ local graph = GraphAdjList.new(edges)
+ print("\n初始化后,图为")
+ graph:print()
+
+ -- 深度优先遍历
+ local res = dfs_traversal(graph, v[1])
+ print("\n深度优先遍历(DFS)顶点序列为")
+ print("[" .. table.concat(vets_to_vals(res), ", ") .. "]")
+end
+
+-- 执行主函数
+main()
diff --git a/codes/lua/chapter_greedy/coin_change_greedy.lua b/codes/lua/chapter_greedy/coin_change_greedy.lua
new file mode 100644
index 0000000000..002a38e083
--- /dev/null
+++ b/codes/lua/chapter_greedy/coin_change_greedy.lua
@@ -0,0 +1,56 @@
+-- @script coin_change_greedy.lua
+-- @date 2025-11-16
+-- @author fisheryv (yue.fisher2025@gdhfi.com)
+
+--- 零钱兑换:贪心
+--- @param coins table 可用的硬币面值列表
+--- @param amt integer 目标金额
+--- @return integer 最少硬币数量,如果无法凑出则返回-1
+local function coin_change_greedy(coins, amt)
+ local i = #coins -- 从最大面额开始
+ local count = 0
+
+ -- 循环进行贪心选择,直到无剩余金额
+ while amt > 0 do
+ -- 找到小于且最接近剩余金额的硬币
+ while i > 1 and coins[i] > amt do
+ i = i - 1
+ end
+
+ -- 选择当前可用的最大面额硬币
+ amt = amt - coins[i]
+ count = count + 1
+ end
+
+ -- 若未找到可行方案,则返回 -1
+ return amt == 0 and count or -1
+end
+
+-- Driver Code
+local function main()
+ -- 测试用例1:贪心能够保证找到全局最优解
+ local coins1 = { 1, 5, 10, 20, 50, 100 }
+ local amt1 = 186
+ local res1 = coin_change_greedy(coins1, amt1)
+ print(string.format("\ncoins = [%s], amt = %d", table.concat(coins1, ", "), amt1))
+ print(string.format("凑到 %d 所需的最少硬币数量为 %d", amt1, res1))
+
+ -- 测试用例2:贪心无法保证找到全局最优解
+ local coins2 = { 1, 20, 50 }
+ local amt2 = 60
+ local res2 = coin_change_greedy(coins2, amt2)
+ print(string.format("\ncoins = [%s], amt = %d", table.concat(coins2, ", "), amt2))
+ print(string.format("凑到 %d 所需的最少硬币数量为 %d", amt2, res2))
+ print("实际上需要的最少数量为 3 ,即 20 + 20 + 20")
+
+ -- 测试用例3:贪心无法保证找到全局最优解
+ local coins3 = { 1, 49, 50 }
+ local amt3 = 98
+ local res3 = coin_change_greedy(coins3, amt3)
+ print(string.format("\ncoins = [%s], amt = %d", table.concat(coins3, ", "), amt3))
+ print(string.format("凑到 %d 所需的最少硬币数量为 %d", amt3, res3))
+ print("实际上需要的最少数量为 2 ,即 49 + 49")
+end
+
+-- 执行主函数
+main()
diff --git a/codes/lua/chapter_greedy/fractional_knapsack.lua b/codes/lua/chapter_greedy/fractional_knapsack.lua
new file mode 100644
index 0000000000..569ef2cf27
--- /dev/null
+++ b/codes/lua/chapter_greedy/fractional_knapsack.lua
@@ -0,0 +1,70 @@
+-- @script fractional_knapsack.lua
+-- @date 2025-11-16
+-- @author fisheryv (yue.fisher2025@gdhfi.com)
+
+--- @class Item
+--- 物品类
+--- @field w number 物品重量
+--- @field v number 物品价值
+local Item = {}
+Item.__index = Item
+
+--- 构造函数
+--- @param w number 物品重量
+--- @param v number 物品价值
+--- @return Item 物品实例
+function Item.new(w, v)
+ local instance = setmetatable({}, Item)
+ instance.w = w
+ instance.v = v
+ return instance
+end
+
+--- 分数背包贪心算法
+--- @param wgt table 物品重量列表
+--- @param val table 物品价值列表
+--- @param cap number 背包容量
+--- @return number 最大价值
+local function fractional_knapsack(wgt, val, cap)
+ -- 创建物品列表
+ local items = {}
+ for i = 1, #wgt do
+ table.insert(items, Item.new(wgt[i], val[i]))
+ end
+
+ -- 按单位价值从高到低排序
+ table.sort(items, function(a, b)
+ return (a.v / a.w) > (b.v / b.w)
+ end)
+
+ local res = 0
+
+ -- 贪心选择
+ for _, item in ipairs(items) do
+ if item.w <= cap then
+ -- 剩余容量充足,装入整个物品
+ res = res + item.v
+ cap = cap - item.w
+ else
+ -- 剩余容量不足,装入部分物品
+ res = res + (item.v / item.w) * cap
+ break
+ end
+ end
+
+ return res
+end
+
+-- Driver Code
+local function main()
+ local wgt = { 10, 20, 30, 40, 50 }
+ local val = { 50, 120, 150, 210, 240 }
+ local cap = 50
+
+ -- 贪心算法
+ local res = fractional_knapsack(wgt, val, cap)
+ print(string.format("不超过背包容量的最大物品价值为 %.2f", res))
+end
+
+-- 执行主函数
+main()
diff --git a/codes/lua/chapter_greedy/max_capacity.lua b/codes/lua/chapter_greedy/max_capacity.lua
new file mode 100644
index 0000000000..3ab0b08cc2
--- /dev/null
+++ b/codes/lua/chapter_greedy/max_capacity.lua
@@ -0,0 +1,39 @@
+-- @script max_capacity.lua
+-- @date 2025-11-16
+-- @author fisheryv (yue.fisher2025@gdhfi.com)
+
+--- 最大容量:贪心
+--- @param ht table 高度数组
+--- @return integer 最大容量
+local function max_capacity(ht)
+ -- 初始化 i, j,使其分列数组两端
+ local i = 1
+ local j = #ht
+ -- 初始最大容量为 0
+ local res = 0
+ -- 循环贪心选择,直至两板相遇
+ while i < j do
+ -- 更新最大容量
+ local cap = math.min(ht[i], ht[j]) * (j - i)
+ res = math.max(res, cap)
+ -- 向内移动短板
+ if ht[i] < ht[j] then
+ i = i + 1
+ else
+ j = j - 1
+ end
+ end
+ return res
+end
+
+-- Driver Code
+local function main()
+ local ht = { 3, 8, 5, 2, 7, 7, 3, 4 }
+
+ -- 贪心算法
+ local res = max_capacity(ht)
+ print(string.format("最大容量为 %d", res))
+end
+
+-- 执行主函数
+main()
diff --git a/codes/lua/chapter_greedy/max_product_cutting.lua b/codes/lua/chapter_greedy/max_product_cutting.lua
new file mode 100644
index 0000000000..e9d8681403
--- /dev/null
+++ b/codes/lua/chapter_greedy/max_product_cutting.lua
@@ -0,0 +1,40 @@
+-- @script max_product_cutting.lua
+-- @date 2025-11-16
+-- @author fisheryv (yue.fisher2025@gdhfi.com)
+
+--- 最大切分乘积:贪心
+--- @param n integer 要切分的整数
+--- @return integer 最大乘积结果
+local function max_product_cutting(n)
+ -- 当 n <= 3 时,必须切分出一个 1
+ if n <= 3 then
+ return 1 * (n - 1)
+ end
+
+ -- 贪心地切分出 3,a 为 3 的个数,b 为余数
+ local a = n // 3
+ local b = n % 3
+
+ if b == 1 then
+ -- 当余数为 1 时,将一对 1 * 3 转化为 2 * 2
+ return 3 ^ (a - 1) * 2 * 2
+ elseif b == 2 then
+ -- 当余数为 2 时,不做处理
+ return 3 ^ a * 2
+ else
+ -- 当余数为 0 时,不做处理
+ return 3 ^ a
+ end
+end
+
+-- Driver Code
+local function main()
+ local n = 58
+
+ -- 贪心算法
+ local res = max_product_cutting(n)
+ print(string.format("最大切分乘积为 %d", res))
+end
+
+-- 执行主函数
+main()
diff --git a/codes/lua/chapter_hashing/array_hash_map.lua b/codes/lua/chapter_hashing/array_hash_map.lua
new file mode 100644
index 0000000000..e332c9fbc5
--- /dev/null
+++ b/codes/lua/chapter_hashing/array_hash_map.lua
@@ -0,0 +1,155 @@
+-- @script: array_hash_map.lua
+-- @date 2025-11-12
+-- @author fisheryv (yue.fisher2025@gdhfi.com)
+
+-- 添加模块搜索路径
+package.path = package.path .. ";codes/lua/modules/?.lua"
+
+local Pair = require("pair")
+
+--- @class ArrayHashMap
+--- 基于数组实现的哈希表
+--- @field buckets table 桶数组
+local ArrayHashMap = {}
+
+---构造方法
+---@return ArrayHashMap
+function ArrayHashMap:new()
+ local obj = {}
+ setmetatable(obj, self)
+ self.__index = self
+ -- 初始化数组,包含100个桶
+ obj.buckets = {}
+ for i = 1, 100 do
+ obj.buckets[i] = ""
+ end
+ return obj
+end
+
+--- 哈希函数
+--- @param key integer 键
+--- @return integer
+function ArrayHashMap:hash_func(key)
+ local index = key % 100 + 1 -- Lua数组索引从1开始
+ return index
+end
+
+--- 查询操作
+--- @param key integer 键
+--- @return string|nil
+function ArrayHashMap:get(key)
+ local index = self:hash_func(key)
+ local pair = self.buckets[index]
+ if pair == "" then
+ return nil
+ end
+ return pair.val
+end
+
+--- 添加和更新操作
+--- @param key integer 键
+--- @param val string 值
+function ArrayHashMap:put(key, val)
+ local pair = Pair:new(key, val)
+ local index = self:hash_func(key)
+ self.buckets[index] = pair
+end
+
+--- 删除操作
+--- @param key integer 键
+function ArrayHashMap:remove(key)
+ local index = self:hash_func(key)
+ -- 置为"",代表删除
+ self.buckets[index] = ""
+end
+
+--- 获取所有键值对
+--- @return table
+function ArrayHashMap:entry_set()
+ local result = {}
+ for _, pair in ipairs(self.buckets) do
+ if pair ~= "" then
+ table.insert(result, pair)
+ end
+ end
+ return result
+end
+
+--- 获取所有键
+--- @return table
+function ArrayHashMap:key_set()
+ local result = {}
+ for _, pair in ipairs(self.buckets) do
+ if pair ~= "" then
+ table.insert(result, pair.key)
+ end
+ end
+ return result
+end
+
+--- 获取所有值
+--- @return table
+function ArrayHashMap:value_set()
+ local result = {}
+ for _, pair in ipairs(self.buckets) do
+ if pair ~= "" then
+ table.insert(result, pair.val)
+ end
+ end
+ return result
+end
+
+--- 打印哈希表
+function ArrayHashMap:print()
+ for _, pair in ipairs(self.buckets) do
+ if pair ~= "" then
+ print(pair.key .. " -> " .. pair.val)
+ end
+ end
+end
+
+-- Driver Code
+local function main()
+ -- 初始化哈希表
+ local hmap = ArrayHashMap:new()
+
+ -- 添加操作
+ -- 在哈希表中添加键值对 (key, value)
+ hmap:put(12836, "小哈")
+ hmap:put(15937, "小啰")
+ hmap:put(16750, "小算")
+ hmap:put(13276, "小法")
+ hmap:put(10583, "小鸭")
+ print("\n添加完成后,哈希表为\nKey -> Value")
+ hmap:print()
+
+ -- 查询操作
+ -- 向哈希表中输入键 key ,得到值 value
+ local name = hmap:get(15937)
+ print("\n输入学号 15937 ,查询到姓名 " .. name)
+
+ -- 删除操作
+ -- 在哈希表中删除键值对 (key, value)
+ hmap:remove(10583)
+ print("\n删除 10583 后,哈希表为\nKey -> Value")
+ hmap:print()
+
+ -- 遍历哈希表
+ print("\n遍历键值对 Key->Value")
+ for _, pair in ipairs(hmap:entry_set()) do
+ print(pair.key .. " -> " .. pair.val)
+ end
+
+ print("\n单独遍历键 Key")
+ for _, key in ipairs(hmap:key_set()) do
+ print(key)
+ end
+
+ print("\n单独遍历值 Value")
+ for _, val in ipairs(hmap:value_set()) do
+ print(val)
+ end
+end
+
+-- 执行主函数
+main()
diff --git a/codes/lua/chapter_hashing/built_in_hash.lua b/codes/lua/chapter_hashing/built_in_hash.lua
new file mode 100644
index 0000000000..d8a2ba1b22
--- /dev/null
+++ b/codes/lua/chapter_hashing/built_in_hash.lua
@@ -0,0 +1 @@
+-- Lua 未提供内置 hash code 函数
\ No newline at end of file
diff --git a/codes/lua/chapter_hashing/hash_map.lua b/codes/lua/chapter_hashing/hash_map.lua
new file mode 100644
index 0000000000..f1eaa8a6a1
--- /dev/null
+++ b/codes/lua/chapter_hashing/hash_map.lua
@@ -0,0 +1,72 @@
+-- @script: hash_map.lua
+-- @date 2025-11-12
+-- @author fisheryv (yue.fisher2025@gdhfi.com)
+
+-- 添加模块搜索路径
+package.path = package.path .. ";codes/lua/modules/?.lua"
+
+local print_util = require("print_util")
+
+--- 根据 key 删除 table 中的元素
+--- @param tbl table
+--- @param key any
+--- @return table
+local function remove_by_key(tbl, key)
+ -- Lua table 不支持按 key 删除元素,因此需要重构整张 table
+ local new_table = {}
+ for _key, _value in pairs(tbl) do
+ if _key ~= key then
+ new_table[_key] = _value
+ end
+ end
+ return new_table
+end
+
+-- Driver Code
+local function main()
+ -- 初始化哈希表
+ -- Lua 内置的表(table)本身就是哈希表
+ local hmap = {}
+
+ -- 添加操作
+ -- 在哈希表中添加键值对 (key, value)
+ hmap[12836] = "小哈"
+ hmap[15937] = "小啰"
+ hmap[16750] = "小算"
+ hmap[13276] = "小法"
+ hmap[10583] = "小鸭"
+ print("\n添加完成后,哈希表为\nKey -> Value")
+ print_util.print_dict(hmap)
+
+ -- 查询操作
+ -- 向哈希表中输入键 key ,得到值 value
+ local name = hmap[15937]
+ print("\n输入学号 15937 ,查询到姓名 " .. name)
+
+ -- 删除操作
+ -- 在哈希表中删除键值对 (key, value)
+ hmap = remove_by_key(hmap, 10583)
+ print("\n删除 10583 后,哈希表为\nKey -> Value")
+ print_util.print_dict(hmap)
+
+ -- 遍历哈希表 - 键值对
+ print("\n遍历键值对 Key->Value")
+ for key, value in pairs(hmap) do
+ print(key .. " -> " .. value)
+ end
+
+ -- 单独遍历键
+ print("\n单独遍历键 Key")
+ for key in pairs(hmap) do
+ print(key)
+ end
+
+ -- 单独遍历值
+ print("\n单独遍历值 Value")
+ for _, value in pairs(hmap) do
+ print(value)
+ end
+end
+
+-- 执行主函数
+main()
diff --git a/codes/lua/chapter_hashing/hash_map_chaining.lua b/codes/lua/chapter_hashing/hash_map_chaining.lua
new file mode 100644
index 0000000000..da4dcd2a05
--- /dev/null
+++ b/codes/lua/chapter_hashing/hash_map_chaining.lua
@@ -0,0 +1,171 @@
+-- @script: hash_map_chaining.lua
+-- @date 2025-11-12
+-- @author fisheryv (yue.fisher2025@gdhfi.com)
+
+-- 添加模块搜索路径
+package.path = package.path .. ";codes/lua/modules/?.lua"
+
+local Pair = require("pair")
+
+--- @class HashMapChaining
+--- 链式地址哈希表
+--- @field size integer 键值对数量
+--- @field capacity integer 哈希表容量
+--- @field load_thres number 触发扩容的负载因子阈值
+--- @field extend_ratio integer 扩容倍数
+local HashMapChaining = {}
+HashMapChaining.__index = HashMapChaining
+
+--- 构造方法
+--- @return HashMapChaining
+function HashMapChaining.new()
+ local obj = {}
+ setmetatable(obj, HashMapChaining)
+ obj.size = 0 -- 键值对数量
+ obj.capacity = 4 -- 哈希表容量
+ obj.load_thres = 2.0 / 3.0 -- 触发扩容的负载因子阈值
+ obj.extend_ratio = 2 -- 扩容倍数
+ obj.buckets = {} -- 桶数组
+
+ -- 初始化桶数组
+ for i = 1, obj.capacity do
+ obj.buckets[i] = ""
+ end
+
+ return obj
+end
+
+--- 哈希函数
+--- @param key integer 键
+--- @return integer
+function HashMapChaining:hash_func(key)
+ return key % self.capacity + 1 -- Lua 数组索引从1开始
+end
+
+--- 负载因子
+--- @return number
+function HashMapChaining:load_factor()
+ return self.size / self.capacity
+end
+
+--- 查询操作
+--- @param key integer 键
+--- @return string|nil
+function HashMapChaining:get(key)
+ local index = self:hash_func(key)
+ local bucket = self.buckets[index]
+
+ -- 遍历桶,若找到 key ,则返回对应 val
+ for _, pair in ipairs(bucket) do
+ if pair.key == key then
+ return pair.val
+ end
+ end
+
+ -- 若未找到 key ,则返回 nil
+ return nil
+end
+
+--- 添加操作
+--- @param key integer 键
+--- @param val string 值
+function HashMapChaining:put(key, val)
+ -- 当负载因子超过阈值时,执行扩容
+ if self:load_factor() > self.load_thres then
+ self:extend()
+ end
+
+ local index = self:hash_func(key)
+ local bucket = self.buckets[index]
+
+ -- 遍历桶,若遇到指定 key ,则更新对应 val 并返回
+ for _, pair in ipairs(bucket) do
+ if pair.key == key then
+ pair.val = val
+ return
+ end
+ end
+
+ -- 若无该 key ,则将键值对添加至尾部
+ local pair = Pair:new(key, val)
+ table.insert(bucket, pair)
+ self.size = self.size + 1
+end
+
+--- 删除操作
+--- @param key integer 键
+function HashMapChaining:remove(key)
+ local index = self:hash_func(key)
+ local bucket = self.buckets[index]
+
+ -- 遍历桶,从中删除键值对
+ for i, pair in ipairs(bucket) do
+ if pair.key == key then
+ table.remove(bucket, i)
+ self.size = self.size - 1
+ break
+ end
+ end
+end
+
+--- 扩容哈希表
+function HashMapChaining:extend()
+ -- 暂存原哈希表
+ local old_buckets = self.buckets
+
+ -- 初始化扩容后的新哈希表
+ self.capacity = self.capacity * self.extend_ratio
+ self.buckets = {}
+ for i = 1, self.capacity do
+ self.buckets[i] = ""
+ end
+ self.size = 0
+
+ -- 将键值对从原哈希表搬运至新哈希表
+ for _, bucket in ipairs(old_buckets) do
+ for _, pair in ipairs(bucket) do
+ self:put(pair.key, pair.val)
+ end
+ end
+end
+
+--- 打印哈希表
+function HashMapChaining:print()
+ for _, bucket in ipairs(self.buckets) do
+ local res = {}
+ for _, pair in ipairs(bucket) do
+ table.insert(res, tostring(pair.key) .. " -> " .. pair.val)
+ end
+ print("[" .. table.concat(res, ", ") .. "]")
+ end
+end
+
+-- Driver Code
+local function main()
+ -- 初始化哈希表
+ local hashmap = HashMapChaining.new()
+
+ -- 添加操作
+ -- 在哈希表中添加键值对 (key, value)
+ hashmap:put(12836, "小哈")
+ hashmap:put(15937, "小啰")
+ hashmap:put(16750, "小算")
+ hashmap:put(13276, "小法")
+ hashmap:put(10583, "小鸭")
+ print("\n添加完成后,哈希表为\n[Key1 -> Value1, Key2 -> Value2, ...]")
+ hashmap:print()
+
+ -- 查询操作
+ -- 向哈希表中输入键 key ,得到值 value
+ local name = hashmap:get(13276)
+ print("\n输入学号 13276 ,查询到姓名 " .. name)
+
+ -- 删除操作
+ -- 在哈希表中删除键值对 (key, value)
+ hashmap:remove(12836)
+ print("\n删除 12836 后,哈希表为\n[Key1 -> Value1, Key2 -> Value2, ...]")
+ hashmap:print()
+end
+
+-- 执行主函数
+main()
diff --git a/codes/lua/chapter_hashing/hash_map_open_addressing.lua b/codes/lua/chapter_hashing/hash_map_open_addressing.lua
new file mode 100644
index 0000000000..e63f1821a1
--- /dev/null
+++ b/codes/lua/chapter_hashing/hash_map_open_addressing.lua
@@ -0,0 +1,205 @@
+-- @script: hash_map_open_addressing.lua
+-- @date 2025-11-12
+-- @author fisheryv (yue.fisher2025@gdhfi.com)
+
+-- 添加模块搜索路径
+package.path = package.path .. ";codes/lua/modules/?.lua"
+
+local Pair = require("pair")
+
+--- @class HashMapOpenAddressing
+--- 开放寻址哈希表
+--- @field size integer 键值对数量
+--- @field capacity integer 哈希表容量
+--- @field load_thres number 触发扩容的负载因子阈值
+--- @field extend_ratio integer 扩容倍数
+--- @field buckets table 桶数组
+--- @field TOMBSTONE Pair 删除标记
+local HashMapOpenAddressing = {}
+HashMapOpenAddressing.__index = HashMapOpenAddressing
+
+--- 构造方法
+--- @return HashMapOpenAddressing
+function HashMapOpenAddressing.new()
+ local obj = setmetatable({}, HashMapOpenAddressing)
+ obj.size = 0 -- 键值对数量
+ obj.capacity = 4 -- 哈希表容量
+ obj.load_thres = 2.0 / 3.0 -- 触发扩容的负载因子阈值
+ obj.extend_ratio = 2 -- 扩容倍数
+ obj.buckets = {} -- 桶数组
+ obj.TOMBSTONE = Pair:new(-1, "-1") -- 删除标记
+
+ -- 初始化桶数组
+ for i = 1, obj.capacity do
+ obj.buckets[i] = ""
+ end
+
+ return obj
+end
+
+--- 哈希函数
+--- @param key integer 键
+--- @return integer
+function HashMapOpenAddressing:hash_func(key)
+ return (key % self.capacity) + 1 -- Lua 数组索引从 1 开始
+end
+
+--- 计算负载因子
+--- @return number
+function HashMapOpenAddressing:load_factor()
+ return self.size / self.capacity
+end
+
+--- 搜索 key 对应的桶索引
+--- @param key integer 要搜索的键
+--- @return integer
+function HashMapOpenAddressing:find_bucket(key)
+ local index = self:hash_func(key)
+ local first_tombstone = -1
+
+ -- 线性探测,当遇到空桶时跳出
+ while self.buckets[index] ~= "" do
+ -- 若遇到 key,返回对应的桶索引
+ if self.buckets[index].key == key then
+ -- 若之前遇到了删除标记,则将键值对移动至该索引处
+ if first_tombstone ~= -1 then
+ self.buckets[first_tombstone] = self.buckets[index]
+ self.buckets[index] = self.TOMBSTONE
+ return first_tombstone -- 返回移动后的桶索引
+ end
+ return index -- 返回桶索引
+ end
+
+ -- 记录遇到的首个删除标记
+ if first_tombstone == -1 and self.buckets[index] == self.TOMBSTONE then
+ first_tombstone = index
+ end
+
+ -- 计算桶索引,越过尾部则返回头部
+ index = (index % self.capacity) + 1
+ end
+
+ -- 若 key 不存在,则返回添加点的索引
+ return first_tombstone == -1 and index or first_tombstone
+end
+
+--- 查询操作
+--- @param key integer 要查询的键
+--- @return string|nil
+function HashMapOpenAddressing:get(key)
+ -- 搜索 key 对应的桶索引
+ local index = self:find_bucket(key)
+
+ -- 若找到键值对,则返回对应 val
+ if self.buckets[index] ~= "" and self.buckets[index] ~= self.TOMBSTONE then
+ return self.buckets[index].val
+ end
+
+ -- 若键值对不存在,则返回 nil
+ return nil
+end
+
+--- 添加操作
+--- @param key integer 键
+--- @param val string 值
+function HashMapOpenAddressing:put(key, val)
+ -- 当负载因子超过阈值时,执行扩容
+ if self:load_factor() > self.load_thres then
+ self:extend()
+ end
+
+ -- 搜索 key 对应的桶索引
+ local index = self:find_bucket(key)
+
+ -- 若找到键值对,则覆盖 val 并返回
+ if self.buckets[index] ~= "" and self.buckets[index] ~= self.TOMBSTONE then
+ self.buckets[index].val = val
+ return
+ end
+
+ -- 若键值对不存在,则添加该键值对
+ self.buckets[index] = Pair:new(key, val)
+ self.size = self.size + 1
+end
+
+--- 删除操作
+--- @param key integer 要删除的键
+function HashMapOpenAddressing:remove(key)
+ -- 搜索 key 对应的桶索引
+ local index = self:find_bucket(key)
+
+ -- 若找到键值对,则用删除标记覆盖它
+ if self.buckets[index] ~= "" and self.buckets[index] ~= self.TOMBSTONE then
+ self.buckets[index] = self.TOMBSTONE
+ self.size = self.size - 1
+ end
+end
+
+--- 扩容哈希表
+function HashMapOpenAddressing:extend()
+ -- 暂存原哈希表
+ local buckets_tmp = {}
+ for i, v in ipairs(self.buckets) do
+ buckets_tmp[i] = v
+ end
+
+ -- 初始化扩容后的新哈希表
+ self.capacity = self.capacity * self.extend_ratio
+ self.buckets = {}
+ self.size = 0
+
+ -- 初始化新桶数组
+ for i = 1, self.capacity do
+ self.buckets[i] = ""
+ end
+
+ -- 将键值对从原哈希表搬运至新哈希表
+ for _, pair in ipairs(buckets_tmp) do
+ if pair ~= "" and pair ~= self.TOMBSTONE then
+ self:put(pair.key, pair.val)
+ end
+ end
+end
+
+--- 打印哈希表
+function HashMapOpenAddressing:print()
+ for i, pair in ipairs(self.buckets) do
+ if pair == "" then
+ print("None")
+ elseif pair == self.TOMBSTONE then
+ print("TOMBSTONE")
+ else
+ print(pair.key .. " -> " .. pair.val)
+ end
+ end
+end
+
+-- Driver Code
+local function main()
+ -- 初始化哈希表
+ local hashmap = HashMapOpenAddressing.new()
+
+ -- 添加操作
+ -- 在哈希表中添加键值对 (key, val)
+ hashmap:put(12836, "小哈")
+ hashmap:put(15937, "小啰")
+ hashmap:put(16750, "小算")
+ hashmap:put(13276, "小法")
+ hashmap:put(10583, "小鸭")
+ print("\n添加完成后,哈希表为\nKey -> Value")
+ hashmap:print()
+
+ -- 查询操作
+ -- 向哈希表中输入键 key ,得到值 val
+ local name = hashmap:get(13276)
+ print("\n输入学号 13276 ,查询到姓名 " .. name)
+
+ -- 删除操作
+ -- 在哈希表中删除键值对 (key, val)
+ hashmap:remove(16750)
+ print("\n删除 16750 后,哈希表为\nKey -> Value")
+ hashmap:print()
+end
+
+-- 执行主函数
+main()
diff --git a/codes/lua/chapter_hashing/simple_hash.lua b/codes/lua/chapter_hashing/simple_hash.lua
new file mode 100644
index 0000000000..76e11c7c9e
--- /dev/null
+++ b/codes/lua/chapter_hashing/simple_hash.lua
@@ -0,0 +1,79 @@
+-- @script: simple_hash.lua
+-- @date 2025-11-12
+-- @author fisheryv (yue.fisher2025@gdhfi.com)
+
+
+--- 加法哈希
+--- @param key string 输入字符串
+--- @return integer
+local function add_hash(key)
+ local hash = 0
+ local modulus = 1000000007
+ for i = 1, #key do
+ local c = string.byte(key, i)
+ hash = hash + c
+ end
+ return hash % modulus
+end
+
+--- 乘法哈希
+--- @param key string 输入字符串
+--- @return integer
+local function mul_hash(key)
+ local hash = 0
+ local modulus = 1000000007
+ for i = 1, #key do
+ local c = string.byte(key, i)
+ hash = 31 * hash + c
+ end
+ return hash % modulus
+end
+
+--- 异或哈希
+--- @param key string 输入字符串
+--- @return integer
+--- @warning Lua 5.3及以上版本才支持位运算符
+local function xor_hash(key)
+ local hash = 0
+ local modulus = 1000000007
+ for i = 1, #key do
+ local c = string.byte(key, i)
+ hash = hash ~ c
+ end
+ return hash % modulus
+end
+
+--- 旋转哈希
+--- @param key string 输入字符串
+--- @return integer
+--- @warning Lua 5.3及以上版本才支持位运算符
+local function rot_hash(key)
+ local hash = 0
+ local modulus = 1000000007
+ for i = 1, #key do
+ local c = string.byte(key, i)
+ hash = ((hash << 4) ~ (hash >> 28) ~ c) & 0x7FFFFFFF
+ end
+ return hash % modulus
+end
+
+
+-- Driver Code
+local function main()
+ local key = "Hello 算法"
+
+ local hash = add_hash(key)
+ print(string.format("加法哈希值为 %d", hash))
+
+ hash = mul_hash(key)
+ print(string.format("乘法哈希值为 %d", hash))
+
+ hash = xor_hash(key)
+ print(string.format("异或哈希值为 %d", hash))
+
+ hash = rot_hash(key)
+ print(string.format("旋转哈希值为 %d", hash))
+end
+
+-- 执行主函数
+main()
diff --git a/codes/lua/chapter_heap/heap.lua b/codes/lua/chapter_heap/heap.lua
new file mode 100644
index 0000000000..2a14a37e80
--- /dev/null
+++ b/codes/lua/chapter_heap/heap.lua
@@ -0,0 +1,94 @@
+-- @script heap.lua
+-- @date 2025-11-14
+-- @author fisheryv (yue.fisher2025@gdhfi.com)
+
+-- 添加模块搜索路径
+package.path = package.path .. ";codes/lua/modules/?.lua"
+
+local print_util = require("print_util")
+-- Lua 没有内置堆模块,我手动实现了一个
+local heapq = require("heapq")
+
+--- 元素入堆测试
+--- @param heap table 堆数组
+--- @param val integer 要插入的值
+--- @param flag integer 堆类型标志 (1: 小顶堆, -1: 大顶堆)
+local function test_push(heap, val, flag)
+ flag = flag or 1
+ heapq.heappush(heap, flag * val)
+
+ print(string.format("\n元素 %d 入堆后", val))
+ -- 打印堆时恢复原始值
+ local display_heap = {}
+ for _, v in ipairs(heap) do
+ table.insert(display_heap, flag * v)
+ end
+ print_util.print_heap(display_heap)
+end
+
+--- 堆顶元素出堆测试
+--- @param heap table 堆数组
+--- @param flag integer 堆类型标志 (1: 小顶堆, -1: 大顶堆)
+local function test_pop(heap, flag)
+ flag = flag or 1
+ local val = flag * heapq.heappop(heap)
+
+ print(string.format("\n堆顶元素 %d 出堆后", val))
+ -- 打印堆时恢复原始值
+ local display_heap = {}
+ for _, v in ipairs(heap) do
+ table.insert(display_heap, flag * v)
+ end
+ print_util.print_heap(display_heap)
+end
+
+
+-- Driver Code
+local function main()
+ -- 初始化小顶堆
+ local min_heap, flag = {}, 1
+
+ -- 初始化大顶堆
+ local max_heap, flag = {}, -1
+
+ print("\n以下测试样例为大顶堆")
+ -- 这里手动实现的 heapq 模块与 Python 保持一致,默认实现小顶堆
+ -- 考虑将"元素取负"后再入堆,这样就可以将大小关系颠倒,从而实现大顶堆
+ -- 在本示例中,flag = 1 时对应小顶堆,flag = -1 时对应大顶堆
+
+ -- 元素入堆
+ test_push(max_heap, 1, flag)
+ test_push(max_heap, 3, flag)
+ test_push(max_heap, 2, flag)
+ test_push(max_heap, 5, flag)
+ test_push(max_heap, 4, flag)
+
+ -- 获取堆顶元素
+ local peek = flag * max_heap[1]
+ print(string.format("\n堆顶元素为 %d", peek))
+
+ -- 堆顶元素出堆
+ test_pop(max_heap, flag)
+ test_pop(max_heap, flag)
+ test_pop(max_heap, flag)
+ test_pop(max_heap, flag)
+ test_pop(max_heap, flag)
+
+ -- 获取堆大小
+ local size = #max_heap
+ print(string.format("\n堆元素数量为 %d", size))
+
+ -- 判断堆是否为空
+ local is_empty = #max_heap == 0
+ print(string.format("\n堆是否为空 %s", tostring(is_empty)))
+
+ -- 输入列表并建堆
+ -- 时间复杂度为 O(n) ,而非 O(nlogn)
+ min_heap = { 1, 3, 2, 5, 4 }
+ heapq.heapify(min_heap)
+ print("\n输入列表并建立小顶堆后")
+ print_util.print_heap(min_heap)
+end
+
+-- 执行主函数
+main()
diff --git a/codes/lua/chapter_heap/my_heap.lua b/codes/lua/chapter_heap/my_heap.lua
new file mode 100644
index 0000000000..e3c5a132e0
--- /dev/null
+++ b/codes/lua/chapter_heap/my_heap.lua
@@ -0,0 +1,193 @@
+-- @script my_heap.lua
+-- @date 2025-11-14
+-- @author fisheryv (yue.fisher2025@gdhfi.com)
+
+-- 添加模块搜索路径
+package.path = package.path .. ";codes/lua/modules/?.lua"
+
+local print_util = require("print_util")
+
+--- @class MaxHeap
+--- 大顶堆实现
+--- @field max_heap table 堆数组
+local MaxHeap = {}
+MaxHeap.__index = MaxHeap
+
+--- 构造方法,根据输入列表建堆
+--- @param nums table 输入数字列表
+--- @return MaxHeap
+function MaxHeap.new(nums)
+ local obj = setmetatable({}, MaxHeap)
+ -- 将列表元素原封不动添加进堆
+ obj.max_heap = {}
+ for i, v in ipairs(nums) do
+ obj.max_heap[i] = v
+ end
+
+ -- 堆化除叶节点以外的其他所有节点
+ local last_non_leaf = math.floor((#obj.max_heap - 1) / 2)
+ for i = last_non_leaf, 1, -1 do
+ obj:sift_down(i)
+ end
+
+ return obj
+end
+
+--- 获取左子节点的索引
+--- @param i integer 当前节点索引
+--- @return integer
+function MaxHeap:left(i)
+ return 2 * i
+end
+
+--- 获取右子节点的索引
+--- @param i integer 当前节点索引
+--- @return integer
+function MaxHeap:right(i)
+ return 2 * i + 1
+end
+
+--- 获取父节点的索引
+--- @param i integer 当前节点索引
+--- @return integer
+function MaxHeap:parent(i)
+ return math.floor(i / 2)
+end
+
+--- 交换元素
+--- @param i integer 索引1
+--- @param j integer 索引2
+function MaxHeap:swap(i, j)
+ self.max_heap[i], self.max_heap[j] = self.max_heap[j], self.max_heap[i]
+end
+
+--- 获取堆大小
+--- @return integer
+function MaxHeap:size()
+ return #self.max_heap
+end
+
+--- 判断堆是否为空
+--- @return boolean
+function MaxHeap:is_empty()
+ return self:size() == 0
+end
+
+--- 访问堆顶元素
+--- @return integer
+function MaxHeap:peek()
+ if self:is_empty() then
+ error("堆为空")
+ end
+ return self.max_heap[1]
+end
+
+--- 元素入堆
+--- @param val integer 要插入的值
+function MaxHeap:push(val)
+ -- 添加节点
+ table.insert(self.max_heap, val)
+ -- 从底至顶堆化
+ self:sift_up(self:size())
+end
+
+--- 从节点 i 开始,从底至顶堆化
+--- @param i integer 节点索引
+function MaxHeap:sift_up(i)
+ while true do
+ -- 获取节点 i 的父节点
+ local p = self:parent(i)
+ -- 当"越过根节点"或"节点无须修复"时,结束堆化
+ if p < 1 or self.max_heap[i] <= self.max_heap[p] then
+ break
+ end
+ -- 交换两节点
+ self:swap(i, p)
+ -- 循环向上堆化
+ i = p
+ end
+end
+
+--- 元素出堆
+--- @return integer
+function MaxHeap:pop()
+ -- 判空处理
+ if self:is_empty() then
+ error("堆为空")
+ end
+ -- 交换根节点与最右叶节点(交换首元素与尾元素)
+ self:swap(1, self:size())
+ -- 删除节点
+ local val = table.remove(self.max_heap)
+ -- 从顶至底堆化
+ if not self:is_empty() then
+ self:sift_down(1)
+ end
+ -- 返回堆顶元素
+ return val
+end
+
+--- 从节点 i 开始,从顶至底堆化
+--- @param i integer 节点索引
+function MaxHeap:sift_down(i)
+ while true do
+ -- 判断节点 i, l, r 中值最大的节点,记为 ma
+ local l = self:left(i)
+ local r = self:right(i)
+ local ma = i
+
+ if l <= self:size() and self.max_heap[l] > self.max_heap[ma] then
+ ma = l
+ end
+ if r <= self:size() and self.max_heap[r] > self.max_heap[ma] then
+ ma = r
+ end
+ -- 若节点 i 最大或索引 l, r 越界,则无须继续堆化,跳出
+ if ma == i then
+ break
+ end
+ -- 交换两节点
+ self:swap(i, ma)
+ -- 循环向下堆化
+ i = ma
+ end
+end
+
+--- 打印堆(二叉树)
+function MaxHeap:print()
+ print_util.print_heap(self.max_heap)
+end
+
+-- 测试代码
+local function main()
+ -- 初始化大顶堆
+ local max_heap = MaxHeap.new({ 9, 8, 6, 6, 7, 5, 2, 1, 4, 3, 6, 2 })
+ print("\n输入列表并建堆后")
+ max_heap:print()
+
+ -- 获取堆顶元素
+ local peek = max_heap:peek()
+ print("\n堆顶元素为 " .. peek)
+
+ -- 元素入堆
+ local val = 7
+ max_heap:push(val)
+ print("\n元素 " .. val .. " 入堆后")
+ max_heap:print()
+
+ -- 堆顶元素出堆
+ peek = max_heap:pop()
+ print("\n堆顶元素 " .. peek .. " 出堆后")
+ max_heap:print()
+
+ -- 获取堆大小
+ local size = max_heap:size()
+ print("\n堆元素数量为 " .. size)
+
+ -- 判断堆是否为空
+ local is_empty = max_heap:is_empty()
+ print("\n堆是否为空 " .. tostring(is_empty))
+end
+
+-- 执行主函数
+main()
diff --git a/codes/lua/chapter_heap/top_k.lua b/codes/lua/chapter_heap/top_k.lua
new file mode 100644
index 0000000000..4e35204798
--- /dev/null
+++ b/codes/lua/chapter_heap/top_k.lua
@@ -0,0 +1,47 @@
+-- @script top_k.lua
+-- @date 2025-11-14
+-- @author fisheryv (yue.fisher2025@gdhfi.com)
+
+-- 添加模块搜索路径
+package.path = package.path .. ";codes/lua/modules/?.lua"
+
+local print_util = require("print_util")
+-- Lua 没有内置堆模块,我手动实现了一个
+local heapq = require("heapq")
+
+--- 基于堆查找数组中最大的 k 个元素
+--- @param nums table 输入数组
+--- @param k integer 要查找的元素个数
+--- @return table 最大的 k 个元素组成的数组
+local function top_k_heap(nums, k)
+ -- 初始化小顶堆
+ local heap = {}
+ -- 将数组的前 k 个元素入堆
+ for i = 1, k do
+ heapq.heappush(heap, nums[i])
+ end
+
+ -- 从第 k+1 个元素开始,保持堆的长度为 k
+ for i = k + 1, #nums do
+ -- 若当前元素大于堆顶元素,则将堆顶元素出堆、当前元素入堆
+ if nums[i] > heap[1] then
+ heapq.heappop(heap)
+ heapq.heappush(heap, nums[i])
+ end
+ end
+
+ return heap
+end
+
+-- Driver Code
+local function main()
+ local nums = { 1, 7, 6, 3, 2 }
+ local k = 3
+
+ local res = top_k_heap(nums, k)
+ print(string.format("最大的 %d 个元素为", k))
+ print_util.print_heap(res)
+end
+
+-- 执行主函数
+main()
diff --git a/codes/lua/chapter_searching/binary_search.lua b/codes/lua/chapter_searching/binary_search.lua
new file mode 100644
index 0000000000..635fa10de5
--- /dev/null
+++ b/codes/lua/chapter_searching/binary_search.lua
@@ -0,0 +1,67 @@
+-- @script binary_search.lua
+-- @date 2025-11-15
+-- @author fisheryv (yue.fisher2025@gdhfi.com)
+
+
+--- 二分查找(双闭区间)
+--- @param nums table 有序数组
+--- @param target integer 目标值
+--- @return integer 目标值在数组中的索引,如果不存在则返回-1
+--- @warning 注意 Lua table 索引从 1 开始
+local function binary_search(nums, target)
+ -- 初始化双闭区间 [0, n-1],即 i, j 分别指向数组首元素、尾元素
+ local i, j = 1, #nums
+ -- 循环,当搜索区间为空时跳出(当 i > j 时为空)
+ while i <= j do
+ -- 计算中点索引 m,防止大数越界
+ local m = math.floor(i + (j - i) / 2)
+ if nums[m] < target then
+ i = m + 1 -- 此情况说明 target 在区间 [m+1, j] 中
+ elseif nums[m] > target then
+ j = m - 1 -- 此情况说明 target 在区间 [i, m-1] 中
+ else
+ return m -- 找到目标元素,返回其索引
+ end
+ end
+ return -1 -- 未找到目标元素,返回 -1
+end
+
+--- 二分查找(左闭右开区间)
+--- @param nums table 有序数组
+--- @param target integer 目标值
+--- @return integer 目标值在数组中的索引,如果不存在则返回-1
+--- @warning 注意 Lua table 索引从 1 开始
+local function binary_search_lcro(nums, target)
+ -- 初始化左闭右开区间 [1, n+1),即 i, j 分别指向数组首元素、尾元素+1
+ local i, j = 1, #nums + 1
+ -- 循环,当搜索区间为空时跳出(当 i = j 时为空)
+ while i < j do
+ -- 计算中点索引 m,防止大数越界
+ local m = math.floor(i + (j - i) / 2)
+ if nums[m] < target then
+ i = m + 1 -- 此情况说明 target 在区间 [m+1, j) 中
+ elseif nums[m] > target then
+ j = m -- 此情况说明 target 在区间 [i, m) 中
+ else
+ return m -- 找到目标元素,返回其索引
+ end
+ end
+ return -1 -- 未找到目标元素,返回 -1
+end
+
+-- Driver Code
+local function main()
+ local target = 6
+ local nums = { 1, 3, 6, 8, 12, 15, 23, 26, 31, 35 }
+
+ -- 二分查找(双闭区间)
+ local index = binary_search(nums, target)
+ print("目标元素 6 的索引 = " .. index)
+
+ -- 二分查找(左闭右开区间)
+ index = binary_search_lcro(nums, target)
+ print("目标元素 6 的索引 = " .. index)
+end
+
+-- 执行主函数
+main()
diff --git a/codes/lua/chapter_searching/binary_search_edge.lua b/codes/lua/chapter_searching/binary_search_edge.lua
new file mode 100644
index 0000000000..cd6d482a2f
--- /dev/null
+++ b/codes/lua/chapter_searching/binary_search_edge.lua
@@ -0,0 +1,61 @@
+-- @script binary_search_edge.lua
+-- @date 2025-11-15
+-- @author fisheryv (yue.fisher2025@gdhfi.com)
+
+-- 添加模块搜索路径
+package.path = package.path .. ";codes/lua/chapter_searching/binary_search_insertion.lua"
+
+local binary_search_insertion = require("binary_search_insertion")
+
+--- 二分查找最左一个 target
+--- @param nums table 有序数组
+--- @param target integer 目标值
+--- @return integer 目标值的最左索引,未找到返回 -1
+--- @warning Lua table 索引从 1 开始
+local function binary_search_left_edge(nums, target)
+ -- 等价于查找 target 的插入点
+ local i = binary_search_insertion(nums, target)
+ -- 未找到 target ,返回 -1
+ if i == #nums + 1 or nums[i] ~= target then
+ return -1
+ end
+ -- 找到 target ,返回索引 i
+ return i
+end
+
+--- 二分查找最右一个 target
+--- @param nums table 有序数组
+--- @param target integer 目标值
+--- @return integer 目标值的最右索引,未找到返回 -1
+--- @warning Lua table 索引从 1 开始
+local function binary_search_right_edge(nums, target)
+ -- 转化为查找最左一个 target + 1
+ local i = binary_search_insertion(nums, target + 1)
+ -- j 指向最右一个 target ,i 指向首个大于 target 的元素
+ local j = i - 1
+ -- 未找到 target ,返回 -1
+ if j == 0 or nums[j] ~= target then
+ return -1
+ end
+ -- 找到 target ,返回索引 j
+ return j
+end
+
+-- Driver Code
+local function main()
+ -- 包含重复元素的数组
+ local nums = { 1, 3, 6, 6, 6, 6, 6, 10, 12, 15 }
+ print(string.format("\n数组 nums = [%s]", table.concat(nums, ", ")))
+
+ -- 二分查找左边界和右边界
+ local targets = { 6, 7 }
+ for _, target in ipairs(targets) do
+ local index = binary_search_left_edge(nums, target)
+ print(string.format("最左一个元素 %d 的索引为 %d", target, index))
+ index = binary_search_right_edge(nums, target)
+ print(string.format("最右一个元素 %d 的索引为 %d", target, index))
+ end
+end
+
+-- 执行主函数
+main()
diff --git a/codes/lua/chapter_searching/binary_search_insertion.lua b/codes/lua/chapter_searching/binary_search_insertion.lua
new file mode 100644
index 0000000000..e1d392efc7
--- /dev/null
+++ b/codes/lua/chapter_searching/binary_search_insertion.lua
@@ -0,0 +1,77 @@
+-- @script binary_search_insertion.lua
+-- @date 2025-11-15
+-- @author fisheryv (yue.fisher2025@gdhfi.com)
+
+--- 二分查找插入点(无重复元素)
+--- @param nums table 有序数组(无重复元素)
+--- @param target integer 目标值
+--- @return integer 插入点索引
+--- @warning Lua table 索引从 1 开始
+local function binary_search_insertion_simple(nums, target)
+ local i, j = 1, #nums -- 初始化双闭区间 [1, n]
+ while i <= j do
+ local m = math.floor((i + j) / 2) -- 计算中点索引 m
+ if nums[m] < target then
+ i = m + 1 -- target 在区间 [m+1, j] 中
+ elseif nums[m] > target then
+ j = m - 1 -- target 在区间 [i, m-1] 中
+ else
+ return m -- 找到 target ,返回插入点 m
+ end
+ end
+ -- 未找到 target ,返回插入点 i
+ return i
+end
+
+--- 二分查找插入点(存在重复元素)
+--- @param nums table 有序数组(可能包含重复元素)
+--- @param target integer 目标值
+--- @return integer 插入点索引
+--- @warning Lua table 索引从 1 开始
+local function binary_search_insertion(nums, target)
+ local i, j = 1, #nums -- 初始化双闭区间 [1, n]
+ while i <= j do
+ local m = math.floor((i + j) / 2) -- 计算中点索引 m
+ if nums[m] < target then
+ i = m + 1 -- target 在区间 [m+1, j] 中
+ elseif nums[m] > target then
+ j = m - 1 -- target 在区间 [i, m-1] 中
+ else
+ j = m - 1 -- 首个小于 target 的元素在区间 [i, m-1] 中
+ end
+ end
+ -- 返回插入点 i
+ return i
+end
+
+-- Driver Code
+local function main()
+ -- 无重复元素的数组
+ local nums1 = { 1, 3, 6, 8, 12, 15, 23, 26, 31, 35 }
+ print(string.format("\n数组 nums = [%s]", table.concat(nums1, ", ")))
+
+ -- 二分查找插入点
+ for _, target in ipairs({ 6, 9 }) do
+ local index = binary_search_insertion_simple(nums1, target)
+ print(string.format("元素 %d 的插入点的索引为 %d", target, index))
+ end
+
+ -- 包含重复元素的数组
+ local nums2 = { 1, 3, 6, 6, 6, 6, 6, 10, 12, 15 }
+ print(string.format("\n数组 nums = [%s]", table.concat(nums2, ", ")))
+
+ -- 二分查找插入点
+ for _, target in ipairs({ 2, 6, 20 }) do
+ local index = binary_search_insertion(nums2, target)
+ print(string.format("元素 %d 的插入点的索引为 %d", target, index))
+ end
+end
+
+
+if ... then
+ -- 被 require 作为模块加载
+ return binary_search_insertion
+else
+ -- 执行主函数
+ main()
+end
diff --git a/codes/lua/chapter_searching/hashing_search.lua b/codes/lua/chapter_searching/hashing_search.lua
new file mode 100644
index 0000000000..61fa54d192
--- /dev/null
+++ b/codes/lua/chapter_searching/hashing_search.lua
@@ -0,0 +1,61 @@
+-- @script hashing_search.lua
+-- @date 2025-11-15
+-- @author fisheryv (yue.fisher2025@gdhfi.com)
+
+-- 添加模块搜索路径
+package.path = package.path .. ";codes/lua/modules/?.lua"
+
+local list_node = require("list_node")
+local list_to_linked_list = list_node.list_to_linked_list
+
+
+--- 哈希查找(数组)
+--- @param hmap table 哈希表,key为目标元素,value为索引
+--- @param target integer 目标值
+--- @return integer 目标元素的索引,不存在则返回-1
+--- @warning Lua table 索引从 1 开始
+local function hashing_search_array(hmap, target)
+ return hmap[target] or -1
+end
+
+--- 哈希查找(链表)
+--- @param hmap table 哈希表,key为目标元素,value为节点对象
+--- @param target integer 目标值
+--- @return ListNode|nil 目标节点对象,不存在则返回nil
+local function hashing_search_linkedlist(hmap, target)
+ return hmap[target]
+end
+
+-- Driver Code
+local function main()
+ local target = 3
+
+ -- 哈希查找(数组)
+ local nums = { 1, 5, 3, 2, 4, 7, 5, 9, 10, 8 }
+
+ -- 初始化哈希表
+ local map0 = {}
+ for i, num in ipairs(nums) do
+ map0[num] = i -- key: 元素,value: 索引
+ end
+
+ local index = hashing_search_array(map0, target)
+ print("目标元素 3 的索引 =", index)
+
+ -- 哈希查找(链表)
+ local head = list_to_linked_list(nums)
+
+ -- 初始化哈希表
+ local map1 = {}
+ local current = head
+ while current do
+ map1[current.val] = current -- key: 节点值,value: 节点
+ current = current.next
+ end
+
+ local node = hashing_search_linkedlist(map1, target)
+ print("目标节点值 3 的对应节点对象为 " .. tostring(node))
+end
+
+-- 执行主函数
+main()
diff --git a/codes/lua/chapter_searching/linear_search.lua b/codes/lua/chapter_searching/linear_search.lua
new file mode 100644
index 0000000000..b3ea878b0a
--- /dev/null
+++ b/codes/lua/chapter_searching/linear_search.lua
@@ -0,0 +1,58 @@
+-- @script linear_search.lua
+-- @date 2025-11-15
+-- @author fisheryv (yue.fisher2025@gdhfi.com)
+
+-- 添加模块搜索路径
+package.path = package.path .. ";codes/lua/modules/?.lua"
+
+local list_node = require("list_node")
+local list_to_linked_list = list_node.list_to_linked_list
+
+--- 线性查找(数组)
+--- @param nums table 数组
+--- @param target integer 目标值
+--- @return integer 找到返回索引,未找到返回-1
+--- @warning Lua table 索引从 1 开始
+local function linear_search_array(nums, target)
+ -- 遍历数组
+ for i = 1, #nums do
+ if nums[i] == target then -- 找到目标元素,返回其索引
+ return i -- 注意:Lua索引从1开始
+ end
+ end
+ return -1 -- 未找到目标元素,返回-1
+end
+
+--- 线性查找(链表)
+--- @param head ListNode|nil 链表头节点
+--- @param target integer 目标值
+--- @return ListNode|nil 找到返回节点,未找到返回nil
+local function linear_search_linkedlist(head, target)
+ -- 遍历链表
+ local current = head
+ while current do
+ if current.val == target then -- 找到目标节点,返回之
+ return current
+ end
+ current = current.next
+ end
+ return nil -- 未找到目标节点,返回nil
+end
+
+-- Driver Code
+local function main()
+ local target = 3
+
+ -- 在数组中执行线性查找
+ local nums = { 1, 5, 3, 2, 4, 7, 5, 9, 10, 8 }
+ local index = linear_search_array(nums, target)
+ print("目标元素 3 的索引 = " .. index)
+
+ -- 在链表中执行线性查找
+ local head = list_to_linked_list(nums)
+ local node = linear_search_linkedlist(head, target)
+ print("目标节点值 3 的对应节点对象为: " .. tostring(node))
+end
+
+-- 执行主函数
+main()
diff --git a/codes/lua/chapter_searching/two_sum.lua b/codes/lua/chapter_searching/two_sum.lua
new file mode 100644
index 0000000000..e38e7a71a1
--- /dev/null
+++ b/codes/lua/chapter_searching/two_sum.lua
@@ -0,0 +1,59 @@
+-- @script two_sum.lua
+-- @date 2025-11-15
+-- @author fisheryv (yue.fisher2025@gdhfi.com)
+
+--- 方法一:暴力枚举
+--- 通过两层循环遍历所有可能的组合
+--- @param nums table 输入的数字数组
+--- @param target integer 目标值
+--- @return table 两个数字的索引组成的数组,如果不存在则返回空表
+--- @warning Lua table 索引从 1 开始
+local function two_sum_brute_force(nums, target)
+ -- 两层循环,时间复杂度为 O(n^2)
+ for i = 1, #nums - 1 do
+ for j = i + 1, #nums do
+ if nums[i] + nums[j] == target then
+ return { i, j } -- 注意:Lua索引从 1 开始
+ end
+ end
+ end
+ return {}
+end
+
+--- 方法二:辅助哈希表
+--- 使用哈希表存储遍历过的数字及其索引
+--- @param nums table 输入的数字数组
+--- @param target integer 目标值
+--- @return table 两个数字的索引组成的数组,如果不存在则返回空表
+local function two_sum_hash_table(nums, target)
+ -- 辅助哈希表,空间复杂度为 O(n)
+ local dic = {}
+ -- 单层循环,时间复杂度为 O(n)
+ for i = 1, #nums do
+ local complement = target - nums[i]
+ if dic[complement] then
+ return { dic[complement], i } -- 注意:Lua索引从 1 开始
+ end
+ dic[nums[i]] = i
+ end
+ return {}
+end
+
+-- Driver Code
+local function main()
+ -- ======= Test Case =======
+ local nums = { 2, 7, 11, 15 }
+ local target = 13
+
+ -- ====== Driver Code ======
+ -- 方法一
+ local res = two_sum_brute_force(nums, target)
+ print("方法一 res = [" .. table.concat(res, ", ") .. "]")
+
+ -- 方法二
+ res = two_sum_hash_table(nums, target)
+ print("方法二 res = [" .. table.concat(res, ", ") .. "]")
+end
+
+-- 执行主函数
+main()
diff --git a/codes/lua/chapter_sorting/bubble_sort.lua b/codes/lua/chapter_sorting/bubble_sort.lua
new file mode 100644
index 0000000000..ec8a0efcf9
--- /dev/null
+++ b/codes/lua/chapter_sorting/bubble_sort.lua
@@ -0,0 +1,54 @@
+-- @script bubble_sort.lua
+-- @date 2025-11-15
+-- @author fisheryv (yue.fisher2025@gdhfi.com)
+
+--- 冒泡排序
+--- @param nums table 待排序的数组
+local function bubble_sort(nums)
+ local n = #nums
+ -- 外循环:未排序区间为 [1, i]
+ for i = n, 2, -1 do
+ -- 内循环:将未排序区间 [1, i] 中的最大元素交换至该区间的最右端
+ for j = 1, i - 1 do
+ if nums[j] > nums[j + 1] then
+ -- 交换 nums[j] 与 nums[j + 1]
+ nums[j], nums[j + 1] = nums[j + 1], nums[j]
+ end
+ end
+ end
+end
+
+--- 冒泡排序(标志优化)
+--- @param nums table 待排序的数组
+local function bubble_sort_with_flag(nums)
+ local n = #nums
+ -- 外循环:未排序区间为 [1, i]
+ for i = n, 2, -1 do
+ local flag = false -- 初始化标志位
+ -- 内循环:将未排序区间 [1, i] 中的最大元素交换至该区间的最右端
+ for j = 1, i - 1 do
+ if nums[j] > nums[j + 1] then
+ -- 交换 nums[j] 与 nums[j + 1]
+ nums[j], nums[j + 1] = nums[j + 1], nums[j]
+ flag = true -- 记录交换元素
+ end
+ end
+ if not flag then
+ break -- 此轮"冒泡"未交换任何元素,直接跳出
+ end
+ end
+end
+
+-- Driver Code
+local function main()
+ local nums = { 4, 1, 3, 1, 5, 2 }
+ bubble_sort(nums)
+ print("冒泡排序完成后 nums = [" .. table.concat(nums, ", ") .. "]")
+
+ local nums1 = { 4, 1, 3, 1, 5, 2 }
+ bubble_sort_with_flag(nums1)
+ print("冒泡排序完成后 nums = [" .. table.concat(nums1, ", ") .. "]")
+end
+
+-- 执行主函数
+main()
diff --git a/codes/lua/chapter_sorting/bucket_sort.lua b/codes/lua/chapter_sorting/bucket_sort.lua
new file mode 100644
index 0000000000..280865a1bf
--- /dev/null
+++ b/codes/lua/chapter_sorting/bucket_sort.lua
@@ -0,0 +1,49 @@
+-- @script bucket_sort.lua
+-- @date 2025-11-15
+-- @author fisheryv (yue.fisher2025@gdhfi.com)
+
+--- 桶排序
+--- @param nums table 待排序的数组(就地修改)
+local function bucket_sort(nums)
+ -- 初始化 k = n/2 个桶,预期向每个桶分配 2 个元素
+ local k = math.floor(#nums / 2)
+ local buckets = {}
+ for i = 1, k do
+ buckets[i] = {}
+ end
+
+ -- 1. 将数组元素分配到各个桶中
+ for _, num in ipairs(nums) do
+ -- 输入数据范围为 [0, 1),使用 num * k 映射到索引范围 [1, k]
+ local idx = math.floor(num * k) + 1
+ -- 确保索引在有效范围内
+ idx = math.max(1, math.min(idx, k))
+ -- 将 num 添加进桶
+ table.insert(buckets[idx], num)
+ end
+
+ -- 2. 对各个桶执行排序
+ for _, bucket in ipairs(buckets) do
+ table.sort(bucket)
+ end
+
+ -- 3. 遍历桶合并结果
+ local i = 1
+ for _, bucket in ipairs(buckets) do
+ for _, num in ipairs(bucket) do
+ nums[i] = num
+ i = i + 1
+ end
+ end
+end
+
+-- Driver Code
+local function main()
+ -- 设输入数据为浮点数,范围为 [0, 1)
+ local nums = { 0.49, 0.96, 0.82, 0.09, 0.57, 0.43, 0.91, 0.75, 0.15, 0.37 }
+ bucket_sort(nums)
+ print("桶排序完成后 nums = [" .. table.concat(nums, ", ") .. "]")
+end
+
+-- 执行主函数
+main()
diff --git a/codes/lua/chapter_sorting/counting_sort.lua b/codes/lua/chapter_sorting/counting_sort.lua
new file mode 100644
index 0000000000..9e5ffffc7a
--- /dev/null
+++ b/codes/lua/chapter_sorting/counting_sort.lua
@@ -0,0 +1,100 @@
+-- @script counting_sort.lua
+-- @date 2025-11-15
+-- @author fisheryv (yue.fisher2025@gdhfi.com)
+
+--- 计数排序的简单实现(无法用于排序对象)
+--- @param nums table 待排序数组
+local function counting_sort_naive(nums)
+ -- 1. 统计数组最大元素 m
+ local m = 0
+ for i = 1, #nums do
+ if nums[i] > m then
+ m = nums[i]
+ end
+ end
+
+ -- 2. 统计各数字的出现次数
+ -- counter[num] 代表 num 的出现次数
+ local counter = {}
+ for i = 0, m do
+ counter[i] = 0
+ end
+
+ for i = 1, #nums do
+ local num = nums[i]
+ counter[num] = counter[num] + 1
+ end
+
+ -- 3. 遍历 counter ,将各元素填入原数组 nums
+ local i = 1
+ for num = 0, m do
+ for _ = 1, counter[num] do
+ nums[i] = num
+ i = i + 1
+ end
+ end
+end
+
+--- 计数排序(完整实现,可排序对象,并且是稳定排序)
+--- @param nums table 待排序数组
+local function counting_sort(nums)
+ -- 1. 统计数组最大元素 m
+ local m = 0
+ for i = 1, #nums do
+ if nums[i] > m then
+ m = nums[i]
+ end
+ end
+
+ -- 2. 统计各数字的出现次数
+ -- counter[num] 代表 num 的出现次数
+ local counter = {}
+ for i = 0, m do
+ counter[i] = 0
+ end
+
+ for i = 1, #nums do
+ local num = nums[i]
+ counter[num] = counter[num] + 1
+ end
+
+ -- 3. 求 counter 的前缀和,将"出现次数"转换为"尾索引"
+ -- 即 counter[num]-1 是 num 在 res 中最后一次出现的索引
+ for i = 0, m - 1 do
+ counter[i + 1] = counter[i + 1] + counter[i]
+ end
+
+ -- 4. 倒序遍历 nums ,将各元素填入结果数组 res
+ -- 初始化数组 res 用于记录结果
+ local n = #nums
+ local res = {}
+ for i = 1, n do
+ res[i] = 0
+ end
+
+ for i = n, 1, -1 do
+ local num = nums[i]
+ res[counter[num]] = num -- 将 num 放置到对应索引处
+ counter[num] = counter[num] - 1 -- 令前缀和自减 1 ,得到下次放置 num 的索引
+ end
+
+ -- 使用结果数组 res 覆盖原数组 nums
+ for i = 1, n do
+ nums[i] = res[i]
+ end
+end
+
+-- Driver Code
+local function main()
+ local nums = { 1, 0, 1, 2, 0, 4, 0, 2, 2, 4 }
+
+ counting_sort_naive(nums)
+ print("计数排序(无法排序对象)完成后 nums = [" .. table.concat(nums, ", ") .. "]")
+
+ local nums1 = { 1, 0, 1, 2, 0, 4, 0, 2, 2, 4 }
+ counting_sort(nums1)
+ print("计数排序完成后 nums1 = [" .. table.concat(nums1, ", ") .. "]")
+end
+
+-- 执行主函数
+main()
diff --git a/codes/lua/chapter_sorting/heap_sort.lua b/codes/lua/chapter_sorting/heap_sort.lua
new file mode 100644
index 0000000000..f916ead7b9
--- /dev/null
+++ b/codes/lua/chapter_sorting/heap_sort.lua
@@ -0,0 +1,72 @@
+-- @script heap_sort.lua
+-- @date 2025-11-15
+-- @author fisheryv (yue.fisher2025@gdhfi.com)
+
+--- 从节点 i 开始,从顶至底堆化
+--- @param nums table 待堆化的数组
+--- @param n integer 堆的长度
+--- @param i integer 当前节点索引
+local function sift_down(nums, n, i)
+ while true do
+ -- 判断节点 i, l, r 中值最大的节点,记为 ma
+ local l = 2 * i + 1
+ local r = 2 * i + 2
+ local ma = i
+
+ if l < n and nums[l + 1] > nums[ma + 1] then
+ ma = l
+ end
+
+ if r < n and nums[r + 1] > nums[ma + 1] then
+ ma = r
+ end
+
+ -- 若节点 i 最大或索引 l, r 越界,则无须继续堆化,跳出
+ if ma == i then
+ break
+ end
+
+ -- 交换两节点
+ nums[i + 1], nums[ma + 1] = nums[ma + 1], nums[i + 1]
+
+ -- 循环向下堆化
+ i = ma
+ end
+end
+
+--- 堆排序
+--- @param nums table 待排序的数组
+--- @return table 排序后的数组
+local function heap_sort(nums)
+ if not nums or #nums == 0 then
+ return nums
+ end
+
+ local n = #nums
+
+ -- 建堆操作:堆化除叶节点以外的其他所有节点
+ for i = math.floor(n / 2) - 1, 0, -1 do
+ sift_down(nums, n, i)
+ end
+
+ -- 从堆中提取最大元素,循环 n-1 轮
+ for i = n - 1, 1, -1 do
+ -- 交换根节点与最右叶节点(交换首元素与尾元素)
+ nums[1], nums[i + 1] = nums[i + 1], nums[1]
+
+ -- 以根节点为起点,从顶至底进行堆化
+ sift_down(nums, i, 0)
+ end
+
+ return nums
+end
+
+-- Driver Code
+local function main()
+ local nums = { 4, 1, 3, 1, 5, 2 }
+ heap_sort(nums)
+ print("堆排序完成后 nums = [" .. table.concat(nums, ", ") .. "]")
+end
+
+-- 执行主函数
+main()
diff --git a/codes/lua/chapter_sorting/insertion_sort.lua b/codes/lua/chapter_sorting/insertion_sort.lua
new file mode 100644
index 0000000000..1ab9bceb7f
--- /dev/null
+++ b/codes/lua/chapter_sorting/insertion_sort.lua
@@ -0,0 +1,29 @@
+-- @script heap_sort.lua
+-- @date 2025-11-15
+-- @author fisheryv (yue.fisher2025@gdhfi.com)
+
+--- 插入排序
+--- @param nums table 待排序的数组
+local function insertion_sort(nums)
+ -- 外循环:已排序区间为 [1, i-1]
+ for i = 2, #nums do
+ local base = nums[i]
+ local j = i - 1
+ -- 内循环:将 base 插入到已排序区间 [1, i-1] 中的正确位置
+ while j >= 1 and nums[j] > base do
+ nums[j + 1] = nums[j] -- 将 nums[j] 向右移动一位
+ j = j - 1
+ end
+ nums[j + 1] = base -- 将 base 赋值到正确位置
+ end
+end
+
+-- Driver Code
+local function main()
+ local nums = { 4, 1, 3, 1, 5, 2 }
+ insertion_sort(nums)
+ print("插入排序完成后 nums = [" .. table.concat(nums, ", ") .. "]")
+end
+
+-- 执行主函数
+main()
diff --git a/codes/lua/chapter_sorting/merge_sort.lua b/codes/lua/chapter_sorting/merge_sort.lua
new file mode 100644
index 0000000000..39583a1d1e
--- /dev/null
+++ b/codes/lua/chapter_sorting/merge_sort.lua
@@ -0,0 +1,78 @@
+-- @script merge_sort.lua
+-- @date 2025-11-15
+-- @author fisheryv (yue.fisher2025@gdhfi.com)
+
+--- 合并左子数组和右子数组
+--- @param nums table 待合并的数组
+--- @param left integer 左边界索引
+--- @param mid integer 中间索引
+--- @param right integer 右边界索引
+local function merge(nums, left, mid, right)
+ -- 左子数组区间为 [left, mid], 右子数组区间为 [mid+1, right]
+ -- 创建一个临时数组 tmp,用于存放合并后的结果
+ local tmp = {}
+ -- 初始化左子数组和右子数组的起始索引
+ local i, j, k = left, mid + 1, 1
+
+ -- 当左右子数组都还有元素时,进行比较并将较小的元素复制到临时数组中
+ while i <= mid and j <= right do
+ if nums[i] <= nums[j] then
+ tmp[k] = nums[i]
+ i = i + 1
+ else
+ tmp[k] = nums[j]
+ j = j + 1
+ end
+ k = k + 1
+ end
+
+ -- 将左子数组的剩余元素复制到临时数组中
+ while i <= mid do
+ tmp[k] = nums[i]
+ i = i + 1
+ k = k + 1
+ end
+
+ -- 将右子数组的剩余元素复制到临时数组中
+ while j <= right do
+ tmp[k] = nums[j]
+ j = j + 1
+ k = k + 1
+ end
+
+ -- 将临时数组 tmp 中的元素复制回原数组 nums 的对应区间
+ for idx = 1, #tmp do
+ nums[left + idx - 1] = tmp[idx]
+ end
+end
+
+--- 归并排序
+--- @param nums table 待排序的数组
+--- @param left integer 左边界索引
+--- @param right integer 右边界索引
+local function merge_sort(nums, left, right)
+ -- 终止条件:当子数组长度为 1 时终止递归
+ if left >= right then
+ return
+ end
+
+ -- 划分阶段:计算中点
+ local mid = math.floor((left + right) / 2)
+
+ -- 递归左子数组
+ merge_sort(nums, left, mid)
+ -- 递归右子数组
+ merge_sort(nums, mid + 1, right)
+ -- 合并阶段
+ merge(nums, left, mid, right)
+end
+
+-- Driver Code
+local function main()
+ local nums = { 7, 3, 2, 6, 0, 1, 5, 4 }
+ merge_sort(nums, 1, #nums)
+ print("归并排序完成后 nums = [" .. table.concat(nums, ", ") .. "]")
+end
+
+-- 执行主函数
+main()
diff --git a/codes/lua/chapter_sorting/quick_sort.lua b/codes/lua/chapter_sorting/quick_sort.lua
new file mode 100644
index 0000000000..2b7708cde8
--- /dev/null
+++ b/codes/lua/chapter_sorting/quick_sort.lua
@@ -0,0 +1,205 @@
+-- @script quick_sort.lua
+-- @date 2025-11-15
+-- @author fisheryv (yue.fisher2025@gdhfi.com)
+
+--- @class QuickSort
+--- 快速排序类
+QuickSort = {}
+QuickSort.__index = QuickSort
+
+--- 创建新的快速排序实例
+--- @return QuickSort 实例
+function QuickSort.new()
+ local obj = {}
+ setmetatable(obj, QuickSort)
+ return obj
+end
+
+--- 哨兵划分
+--- @param nums table 待排序数组
+--- @param left integer 左边界索引
+--- @param right integer 右边界索引
+--- @return integer 基准数索引
+function QuickSort:partition(nums, left, right)
+ -- 以 nums[left] 为基准数
+ local i, j = left, right
+ while i < j do
+ while i < j and nums[j] >= nums[left] do
+ j = j - 1 -- 从右向左找首个小于基准数的元素
+ end
+ while i < j and nums[i] <= nums[left] do
+ i = i + 1 -- 从左向右找首个大于基准数的元素
+ end
+ -- 元素交换
+ nums[i], nums[j] = nums[j], nums[i]
+ end
+ -- 将基准数交换至两子数组的分界线
+ nums[i], nums[left] = nums[left], nums[i]
+ return i -- 返回基准数的索引
+end
+
+--- 快速排序
+--- @param nums table 待排序数组
+--- @param left integer 左边界索引
+--- @param right integer 右边界索引
+function QuickSort:quick_sort(nums, left, right)
+ -- 子数组长度为 1 时终止递归
+ if left >= right then
+ return
+ end
+ -- 哨兵划分
+ local pivot = self:partition(nums, left, right)
+ -- 递归左子数组、右子数组
+ self:quick_sort(nums, left, pivot - 1)
+ self:quick_sort(nums, pivot + 1, right)
+end
+
+--- @class QuickSortMedian
+--- 快速排序类(中位基准数优化)
+QuickSortMedian = {}
+QuickSortMedian.__index = QuickSortMedian
+
+--- 创建新的中位基准数快速排序实例
+-- @return QuickSortMedian 实例
+function QuickSortMedian.new()
+ local obj = {}
+ setmetatable(obj, QuickSortMedian)
+ return obj
+end
+
+--- 选取三个候选元素的中位数
+--- @param nums table 数组
+--- @param left integer 左边界索引
+--- @param mid integer 中间索引
+--- @param right integer 右边界索引
+--- @return integer 中位数索引
+function QuickSortMedian:median_three(nums, left, mid, right)
+ local l, m, r = nums[left], nums[mid], nums[right]
+ if (l <= m and m <= r) or (r <= m and m <= l) then
+ return mid -- m 在 l 和 r 之间
+ end
+ if (m <= l and l <= r) or (r <= l and l <= m) then
+ return left -- l 在 m 和 r 之间
+ end
+ return right
+end
+
+--- 哨兵划分(三数取中值)
+--- @param nums table 待排序数组
+--- @param left integer 左边界索引
+--- @param right integer 右边界索引
+--- @return integer 基准数索引
+function QuickSortMedian:partition(nums, left, right)
+ -- 以 nums[left] 为基准数
+ local med = self:median_three(nums, left, math.floor((left + right) / 2), right)
+ -- 将中位数交换至数组最左端
+ nums[left], nums[med] = nums[med], nums[left]
+ -- 以 nums[left] 为基准数
+ local i, j = left, right
+ while i < j do
+ while i < j and nums[j] >= nums[left] do
+ j = j - 1 -- 从右向左找首个小于基准数的元素
+ end
+ while i < j and nums[i] <= nums[left] do
+ i = i + 1 -- 从左向右找首个大于基准数的元素
+ end
+ -- 元素交换
+ nums[i], nums[j] = nums[j], nums[i]
+ end
+ -- 将基准数交换至两子数组的分界线
+ nums[i], nums[left] = nums[left], nums[i]
+ return i -- 返回基准数的索引
+end
+
+--- 快速排序
+--- @param nums table 待排序数组
+--- @param left integer 左边界索引
+--- @param right integer 右边界索引
+function QuickSortMedian:quick_sort(nums, left, right)
+ -- 子数组长度为 1 时终止递归
+ if left >= right then
+ return
+ end
+ -- 哨兵划分
+ local pivot = self:partition(nums, left, right)
+ -- 递归左子数组、右子数组
+ self:quick_sort(nums, left, pivot - 1)
+ self:quick_sort(nums, pivot + 1, right)
+end
+
+--- @class QuickSortTailCall
+--- 快速排序类(递归深度优化)
+QuickSortTailCall = {}
+QuickSortTailCall.__index = QuickSortTailCall
+
+--- 创建新的尾递归优化快速排序实例
+--- @return QuickSortTailCall 实例
+function QuickSortTailCall.new()
+ local obj = {}
+ setmetatable(obj, QuickSortTailCall)
+ return obj
+end
+
+--- 哨兵划分
+--- @param nums table 待排序数组
+--- @param left integer 左边界索引
+--- @param right integer 右边界索引
+--- @return integer 基准数索引
+function QuickSortTailCall:partition(nums, left, right)
+ -- 以 nums[left] 为基准数
+ local i, j = left, right
+ while i < j do
+ while i < j and nums[j] >= nums[left] do
+ j = j - 1 -- 从右向左找首个小于基准数的元素
+ end
+ while i < j and nums[i] <= nums[left] do
+ i = i + 1 -- 从左向右找首个大于基准数的元素
+ end
+ -- 元素交换
+ nums[i], nums[j] = nums[j], nums[i]
+ end
+ -- 将基准数交换至两子数组的分界线
+ nums[i], nums[left] = nums[left], nums[i]
+ return i -- 返回基准数的索引
+end
+
+--- 快速排序(递归深度优化)
+--- @param nums table 待排序数组
+--- @param left integer 左边界索引
+--- @param right integer 右边界索引
+function QuickSortTailCall:quick_sort(nums, left, right)
+ -- 子数组长度为 1 时终止
+ while left < right do
+ -- 哨兵划分操作
+ local pivot = self:partition(nums, left, right)
+ -- 对两个子数组中较短的那个执行快速排序
+ if pivot - left < right - pivot then
+ self:quick_sort(nums, left, pivot - 1) -- 递归排序左子数组
+ left = pivot + 1 -- 剩余未排序区间为 [pivot + 1, right]
+ else
+ self:quick_sort(nums, pivot + 1, right) -- 递归排序右子数组
+ right = pivot - 1 -- 剩余未排序区间为 [left, pivot - 1]
+ end
+ end
+end
+
+-- Driver Code
+local function main()
+ -- 快速排序
+ local nums = { 2, 4, 1, 0, 3, 5 }
+ QuickSort.new():quick_sort(nums, 1, #nums)
+ print("快速排序完成后 nums = [" .. table.concat(nums, ", ") .. "]")
+
+ -- 快速排序(中位基准数优化)
+ local nums1 = { 2, 4, 1, 0, 3, 5 }
+ QuickSortMedian.new():quick_sort(nums1, 1, #nums1)
+ print("快速排序(中位基准数优化)完成后 nums = [" .. table.concat(nums1, ", ") .. "]")
+
+ -- 快速排序(递归深度优化)
+ local nums2 = { 2, 4, 1, 0, 3, 5 }
+ QuickSortTailCall.new():quick_sort(nums2, 1, #nums2)
+ print("快速排序(递归深度优化)完成后 nums = [" .. table.concat(nums2, ", ") .. "]")
+end
+
+-- 执行主函数
+main()
diff --git a/codes/lua/chapter_sorting/radix_sort.lua b/codes/lua/chapter_sorting/radix_sort.lua
new file mode 100644
index 0000000000..2af6b4fe67
--- /dev/null
+++ b/codes/lua/chapter_sorting/radix_sort.lua
@@ -0,0 +1,96 @@
+-- @script radix_sort.lua
+-- @date 2025-11-15
+-- @author fisheryv (yue.fisher2025@gdhfi.com)
+
+--- 获取元素的第 k 位,其中 exp = 10^(k-1)
+--- @param num integer 要获取位数的数字
+--- @param exp integer 基数,10^(k-1)
+--- @return integer 第 k 位的数字
+local function digit(num, exp)
+ -- 传入 exp 而非 k 可以避免在此重复执行昂贵的次方计算
+ return math.floor(num / exp) % 10
+end
+
+--- 计数排序(根据 nums 第 k 位排序)
+--- @param nums table 要排序的数字数组
+--- @param exp integer 基数,10^(k-1)
+local function counting_sort_digit(nums, exp)
+ -- 十进制的位范围为 0~9 ,因此需要长度为 10 的桶数组
+ local counter = {}
+ for i = 0, 9 do
+ counter[i] = 0
+ end
+
+ local n = #nums
+
+ -- 统计 0~9 各数字的出现次数
+ for i = 1, n do
+ local d = digit(nums[i], exp) -- 获取 nums[i] 第 k 位,记为 d
+ counter[d] = counter[d] + 1 -- 统计数字 d 的出现次数
+ end
+
+ -- 求前缀和,将"出现个数"转换为"数组索引"
+ for i = 1, 9 do
+ counter[i] = counter[i] + counter[i - 1]
+ end
+
+ -- 倒序遍历,根据桶内统计结果,将各元素填入 res
+ local res = {}
+ for i = n, 1, -1 do
+ local d = digit(nums[i], exp)
+ local j = counter[d] -- 获取 d 在数组中的索引 j
+ res[j] = nums[i] -- 将当前元素填入索引 j
+ counter[d] = counter[d] - 1 -- 将 d 的数量减 1
+ end
+
+ -- 使用结果覆盖原数组 nums
+ for i = 1, n do
+ nums[i] = res[i]
+ end
+end
+
+--- 基数排序
+--- @param nums table 要排序的数字数组
+local function radix_sort(nums)
+ -- 获取数组的最大元素,用于判断最大位数
+ local m = nums[1]
+ for i = 2, #nums do
+ if nums[i] > m then
+ m = nums[i]
+ end
+ end
+
+ -- 按照从低位到高位的顺序遍历
+ local exp = 1
+ while exp <= m do
+ -- 对数组元素的第 k 位执行计数排序
+ -- k = 1 -> exp = 1
+ -- k = 2 -> exp = 10
+ -- 即 exp = 10^(k-1)
+ counting_sort_digit(nums, exp)
+ exp = exp * 10
+ end
+end
+
+-- Driver Code
+local function main()
+ -- 基数排序
+ local nums = {
+ 10546151,
+ 35663510,
+ 42865989,
+ 34862445,
+ 81883077,
+ 88906420,
+ 72429244,
+ 30524779,
+ 82060337,
+ 63832996
+ }
+
+ radix_sort(nums)
+ print("基数排序完成后 nums = [" .. table.concat(nums, ", ") .. "]")
+end
+
+-- 执行主函数
+main()
diff --git a/codes/lua/chapter_sorting/selection_sort.lua b/codes/lua/chapter_sorting/selection_sort.lua
new file mode 100644
index 0000000000..bf4cdfdd2c
--- /dev/null
+++ b/codes/lua/chapter_sorting/selection_sort.lua
@@ -0,0 +1,31 @@
+-- @script selection_sort.lua
+-- @date 2025-11-15
+-- @author fisheryv (yue.fisher2025@gdhfi.com)
+
+--- 选择排序
+--- @param nums table 待排序的数组(原地修改)
+local function selection_sort(nums)
+ local n = #nums
+ -- 外循环:未排序区间为 [i, n]
+ for i = 1, n - 1 do
+ -- 内循环:找到未排序区间内的最小元素
+ local k = i
+ for j = i + 1, n do
+ if nums[j] < nums[k] then
+ k = j -- 记录最小元素的索引
+ end
+ end
+ -- 将该最小元素与未排序区间的首个元素交换
+ nums[i], nums[k] = nums[k], nums[i]
+ end
+end
+
+-- Driver Code
+local function main()
+ local nums = { 4, 1, 3, 1, 5, 2 }
+ selection_sort(nums)
+ print("选择排序完成后 nums = [" .. table.concat(nums, ", ") .. "]")
+end
+
+-- 执行主函数
+main()
diff --git a/codes/lua/chapter_stack_and_queue/array_deque.lua b/codes/lua/chapter_stack_and_queue/array_deque.lua
new file mode 100644
index 0000000000..9c7f4ef978
--- /dev/null
+++ b/codes/lua/chapter_stack_and_queue/array_deque.lua
@@ -0,0 +1,175 @@
+-- @script: array_deque.lua
+-- @date 2025-11-11
+-- @author fisheryv (yue.fisher2025@gdhfi.com)
+
+--- @class ArrayDeque
+--- 基于环形数组实现的双向队列
+--- @field _nums table 用于存储双向队列元素的数组
+--- @field _front number 队首指针,指向队首元素
+--- @field _size number 双向队列长度
+local ArrayDeque = {}
+ArrayDeque.__index = ArrayDeque
+
+--- 构造方法
+--- @param capacity integer 队列容量
+--- @return ArrayDeque
+function ArrayDeque.new(capacity)
+ local obj = {}
+ setmetatable(obj, ArrayDeque)
+
+ obj._nums = {}
+ for i = 1, capacity do
+ obj._nums[i] = 0
+ end
+ obj._front = 1
+ obj._size = 0
+
+ return obj
+end
+
+--- 获取双向队列的容量
+--- @return integer
+function ArrayDeque:capacity()
+ return #self._nums
+end
+
+--- 获取双向队列的长度
+--- @return integer
+function ArrayDeque:size()
+ return self._size
+end
+
+--- 判断双向队列是否为空
+--- @return boolean
+function ArrayDeque:is_empty()
+ return self._size == 0
+end
+
+--- 计算环形数组索引
+--- @param i integer 原始索引
+--- @return integer
+function ArrayDeque:index(i)
+ -- 通过取余操作实现数组首尾相连
+ -- 当 i 越过数组尾部后,回到头部
+ -- 当 i 越过数组头部后,回到尾部
+ return (i + self:capacity() - 1) % self:capacity() + 1
+end
+
+--- 队首入队
+--- @param num number 要入队的数字
+function ArrayDeque:push_first(num)
+ if self._size == self:capacity() then
+ print("双向队列已满")
+ return
+ end
+ -- 队首指针向左移动一位
+ -- 通过取余操作实现 front 越过数组头部后回到尾部
+ self._front = self:index(self._front - 1)
+ -- 将 num 添加至队首
+ self._nums[self._front] = num
+ self._size = self._size + 1
+end
+
+--- 队尾入队
+--- @param num number 要入队的数字
+function ArrayDeque:push_last(num)
+ if self._size == self:capacity() then
+ print("双向队列已满")
+ return
+ end
+ -- 计算队尾指针,指向队尾索引 + 1
+ local rear = self:index(self._front + self._size)
+ -- 将 num 添加至队尾
+ self._nums[rear] = num
+ self._size = self._size + 1
+end
+
+--- 队首出队
+--- @return number
+function ArrayDeque:pop_first()
+ local num = self:peek_first()
+ -- 队首指针向后移动一位
+ self._front = self:index(self._front + 1)
+ self._size = self._size - 1
+ return num
+end
+
+--- 队尾出队
+--- @return number
+function ArrayDeque:pop_last()
+ local num = self:peek_last()
+ self._size = self._size - 1
+ return num
+end
+
+--- 访问队首元素
+--- @return number
+--- @raise 双向队列为空时抛出错误
+function ArrayDeque:peek_first()
+ if self:is_empty() then
+ error("双向队列为空")
+ end
+ return self._nums[self._front]
+end
+
+--- 访问队尾元素
+--- @return number
+--- @raise 双向队列为空时抛出错误
+function ArrayDeque:peek_last()
+ if self:is_empty() then
+ error("双向队列为空")
+ end
+ -- 计算尾元素索引
+ local last = self:index(self._front + self._size - 1)
+ return self._nums[last]
+end
+
+--- 返回数组用于打印
+--- @return table
+function ArrayDeque:to_array()
+ local res = {}
+ -- 仅转换有效长度范围内的列表元素
+ for i = 0, self._size - 1 do
+ res[i + 1] = self._nums[self:index(self._front + i)]
+ end
+ return res
+end
+
+-- Driver Code
+local function main()
+ -- 初始化双向队列
+ local deque = ArrayDeque.new(10)
+ deque:push_last(3)
+ deque:push_last(2)
+ deque:push_last(5)
+ print("双向队列 deque = [" .. table.concat(deque:to_array(), ", ") .. "]")
+
+ -- 访问元素
+ local peek_first = deque:peek_first()
+ print("队首元素 peek_first = " .. tostring(peek_first))
+ local peek_last = deque:peek_last()
+ print("队尾元素 peek_last = " .. tostring(peek_last))
+
+ -- 元素入队
+ deque:push_last(4)
+ print("元素 4 队尾入队后 deque = [" .. table.concat(deque:to_array(), ", ") .. "]")
+ deque:push_first(1)
+ print("元素 1 队首入队后 deque = [" .. table.concat(deque:to_array(), ", ") .. "]")
+
+ -- 元素出队
+ local pop_last = deque:pop_last()
+ print(string.format("队尾出队元素 = %d ,队尾出队后 deque = [%s]", pop_last, table.concat(deque:to_array(), ", ")))
+ local pop_first = deque:pop_first()
+ print(string.format("队首出队元素 = %d ,队首出队后 deque = [%s]", pop_first, table.concat(deque:to_array(), ", ")))
+
+ -- 获取双向队列的长度
+ local size = deque:size()
+ print("双向队列长度 size = " .. tostring(size))
+
+ -- 判断双向队列是否为空
+ local is_empty = deque:is_empty()
+ print("双向队列是否为空 = " .. tostring(is_empty))
+end
+
+-- 执行主函数
+main()
diff --git a/codes/lua/chapter_stack_and_queue/array_queue.lua b/codes/lua/chapter_stack_and_queue/array_queue.lua
new file mode 100644
index 0000000000..c7ad4892a5
--- /dev/null
+++ b/codes/lua/chapter_stack_and_queue/array_queue.lua
@@ -0,0 +1,144 @@
+-- @script: array_queue.lua
+-- @date 2025-11-11
+-- @author fisheryv (yue.fisher2025@gdhfi.com)
+
+--- @class ArrayQueue
+--- 基于环形数组实现的队列
+--- @field _nums table 用于存储队列元素的数组
+--- @field _front integer 队首指针,指向队首元素
+--- @field _size integer 队列长度
+local ArrayQueue = {}
+ArrayQueue.__index = ArrayQueue
+
+--- 构造方法
+--- @param size integer 队列容量
+--- @return ArrayQueue 实例
+function ArrayQueue.new(size)
+ local obj = {}
+ setmetatable(obj, ArrayQueue)
+
+ -- 用于存储队列元素的数组
+ obj._nums = {}
+ for i = 1, size do
+ obj._nums[i] = 0
+ end
+
+ -- 队首指针,指向队首元素(Lua索引从1开始)
+ obj._front = 1
+ -- 队列长度
+ obj._size = 0
+
+ return obj
+end
+
+--- 获取队列的容量
+--- @return integer
+function ArrayQueue:capacity()
+ return #self._nums
+end
+
+--- 获取队列的长度
+--- @return integer
+function ArrayQueue:size()
+ return self._size
+end
+
+--- 判断队列是否为空
+--- @return boolean
+function ArrayQueue:is_empty()
+ return self._size == 0
+end
+
+--- 入队
+--- @param num integer 要入队的元素
+--- @raise 如果队列已满则抛出错误
+function ArrayQueue:push(num)
+ if self._size == self:capacity() then
+ error("队列已满")
+ end
+
+ -- 计算队尾指针,指向队尾索引 + 1
+ -- 通过取余操作实现rear越过数组尾部后回到头部
+ local rear = (self._front + self._size - 1) % self:capacity() + 1
+ -- 将num添加至队尾
+ self._nums[rear] = num
+ self._size = self._size + 1
+end
+
+--- 出队
+--- @return integer
+--- @raise 队列为空时抛出错误
+function ArrayQueue:pop()
+ local num = self:peek()
+ -- 队首指针向后移动一位,若越过尾部,则返回到数组头部
+ self._front = (self._front % self:capacity()) + 1
+ self._size = self._size - 1
+ return num
+end
+
+--- 访问队首元素
+--- @return integer
+--- @raise 队列为空时抛出错误
+function ArrayQueue:peek()
+ if self:is_empty() then
+ error("队列为空")
+ end
+ return self._nums[self._front]
+end
+
+--- 返回列表用于打印
+--- @return table
+function ArrayQueue:to_list()
+ local res = {}
+ local j = self._front
+
+ for i = 1, self:size() do
+ -- 计算实际索引,处理环形数组的环绕
+ local index = ((j - 1) % self:capacity()) + 1
+ res[i] = self._nums[index]
+ j = j + 1
+ end
+
+ return res
+end
+
+-- Driver Code
+local function main()
+ -- 初始化队列
+ local queue = ArrayQueue.new(10)
+
+ -- 元素入队
+ queue:push(1)
+ queue:push(3)
+ queue:push(2)
+ queue:push(5)
+ queue:push(4)
+ print("队列 queue = [" .. table.concat(queue:to_list(), ", ") .. "]")
+
+ -- 访问队首元素
+ local peek = queue:peek()
+ print("队首元素 peek = " .. tostring(peek))
+
+ -- 元素出队
+ local pop = queue:pop()
+ print("出队元素 pop = " .. tostring(pop))
+ print("出队后 queue = [" .. table.concat(queue:to_list(), ", ") .. "]")
+
+ -- 获取队列的长度
+ local size = queue:size()
+ print("队列长度 size = " .. tostring(size))
+
+ -- 判断队列是否为空
+ local is_empty = queue:is_empty()
+ print("队列是否为空 = " .. tostring(is_empty))
+
+ -- 测试环形数组
+ for i = 0, 9 do
+ queue:push(i)
+ queue:pop()
+ print(string.format("第 %d 轮入队 + 出队后 queue = [%s]", i, table.concat(queue:to_list(), ", ")))
+ end
+end
+
+-- 执行主函数
+main()
diff --git a/codes/lua/chapter_stack_and_queue/array_stack.lua b/codes/lua/chapter_stack_and_queue/array_stack.lua
new file mode 100644
index 0000000000..a0519970a8
--- /dev/null
+++ b/codes/lua/chapter_stack_and_queue/array_stack.lua
@@ -0,0 +1,101 @@
+-- @script: array_stack.lua
+-- @date 2025-11-11
+-- @author fisheryv (yue.fisher2025@gdhfi.com)
+
+--- @class ArrayStack
+--- 基于数组实现的栈
+--- @field _stack table 使用表作为栈的存储结构
+local ArrayStack = {}
+ArrayStack.__index = ArrayStack
+
+--- 构造方法
+--- @return ArrayStack
+function ArrayStack.new()
+ local obj = {}
+ setmetatable(obj, ArrayStack)
+ obj._stack = {} -- 使用表作为栈的存储结构
+ return obj
+end
+
+--- 获取栈的长度
+--- @return integer
+function ArrayStack:size()
+ return #self._stack
+end
+
+--- 判断栈是否为空
+--- @return boolean
+function ArrayStack:is_empty()
+ return self:size() == 0
+end
+
+--- 入栈操作
+--- @param item integer 要入栈的元素
+function ArrayStack:push(item)
+ table.insert(self._stack, item)
+end
+
+--- 出栈操作
+--- @return integer
+--- @raise 栈为空时抛出错误
+function ArrayStack:pop()
+ if self:is_empty() then
+ error("栈为空")
+ end
+ return table.remove(self._stack)
+end
+
+--- 访问栈顶元素
+--- @return integer
+--- @raise 栈为空时抛出错误
+function ArrayStack:peek()
+ if self:is_empty() then
+ error("栈为空")
+ end
+ return self._stack[#self._stack]
+end
+
+--- 返回列表用于打印
+--- @return table
+function ArrayStack:to_list()
+ -- 返回栈的浅拷贝以避免外部修改
+ local result = {}
+ for i, v in ipairs(self._stack) do
+ result[i] = v
+ end
+ return result
+end
+
+-- Driver Code
+local function main()
+ -- 初始化栈
+ local stack = ArrayStack.new()
+
+ -- 元素入栈
+ stack:push(1)
+ stack:push(3)
+ stack:push(2)
+ stack:push(5)
+ stack:push(4)
+ print("栈 stack = [" .. table.concat(stack:to_list(), ", ") .. "]")
+
+ -- 访问栈顶元素
+ local peek = stack:peek()
+ print("栈顶元素 peek = " .. peek)
+
+ -- 元素出栈
+ local pop = stack:pop()
+ print("出栈元素 pop = " .. pop)
+ print("出栈后 stack = [" .. table.concat(stack:to_list(), ", ") .. "]")
+
+ -- 获取栈的长度
+ local size = stack:size()
+ print("栈的长度 size = " .. size)
+
+ -- 判断是否为空
+ local is_empty = stack:is_empty()
+ print("栈是否为空 = " .. tostring(is_empty))
+end
+
+-- 执行主函数
+main()
diff --git a/codes/lua/chapter_stack_and_queue/deque.lua b/codes/lua/chapter_stack_and_queue/deque.lua
new file mode 100644
index 0000000000..e02862a749
--- /dev/null
+++ b/codes/lua/chapter_stack_and_queue/deque.lua
@@ -0,0 +1,44 @@
+-- @script dequeue.lua
+-- @date 2025-11-11
+-- @author fisheryv (yue.fisher2025@gdhfi.com)
+
+
+-- Driver Code
+local function main()
+ -- 使用Lua表模拟双向队列数据结构
+ -- 初始化双向队列
+ local deq = {}
+
+ -- 元素入队
+ table.insert(deq, 2) -- 添加至队尾
+ table.insert(deq, 5)
+ table.insert(deq, 4)
+ table.insert(deq, 1, 3) -- 添加至队首
+ table.insert(deq, 1, 1)
+ print("双向队列 deque = [" .. table.concat(deq, ", ") .. "]")
+
+ -- 访问元素
+ local front = deq[1] -- 队首元素
+ print("队首元素 front = " .. tostring(front))
+ local rear = deq[#deq] -- 队尾元素
+ print("队尾元素 rear = " .. tostring(rear))
+
+ -- 元素出队
+ local pop_front = table.remove(deq, 1) -- 队首元素出队
+ print("队首出队元素 pop_front = " .. tostring(pop_front))
+ print("队首出队后 deque = [" .. table.concat(deq, ", ") .. "]")
+ local pop_rear = table.remove(deq) -- 队尾元素出队
+ print("队尾出队元素 pop_rear = " .. tostring(pop_rear))
+ print("队尾出队后 deque = [" .. table.concat(deq, ", ") .. "]")
+
+ -- 获取双向队列的长度
+ local size = #deq
+ print("双向队列长度 size = " .. tostring(size))
+
+ -- 判断双向队列是否为空
+ local is_empty = #deq == 0
+ print("双向队列是否为空 = " .. tostring(is_empty))
+end
+
+-- 执行主函数
+main()
diff --git a/codes/lua/chapter_stack_and_queue/linkedlist_deque.lua b/codes/lua/chapter_stack_and_queue/linkedlist_deque.lua
new file mode 100644
index 0000000000..ea64da12d4
--- /dev/null
+++ b/codes/lua/chapter_stack_and_queue/linkedlist_deque.lua
@@ -0,0 +1,208 @@
+-- @script linked_deque.lua
+-- @date 2025-11-11
+-- @author fisheryv (yue.fisher2025@gdhfi.com)
+
+--- @class DeListNode
+--- 双向链表节点
+--- @field val number 节点值
+--- @field next DeListNode|nil 后继节点引用
+--- @field prev DeListNode|nil 前驱节点引用
+local DeListNode = {}
+DeListNode.__index = DeListNode
+
+--- 构造方法
+--- @param val number 节点值
+--- @return DeListNode
+function DeListNode.new(val)
+ local obj = {}
+ setmetatable(obj, DeListNode)
+ obj.val = val
+ obj.next = nil -- 后继节点引用
+ obj.prev = nil -- 前驱节点引用
+ return obj
+end
+
+--- @class LinkedListDeque
+--- 基于双向链表实现的双向队列
+--- @field _front DeListNode|nil 队首节点
+--- @field _rear DeListNode|nil 队尾节点
+--- @field _size integer 双向队列长度
+local LinkedListDeque = {}
+LinkedListDeque.__index = LinkedListDeque
+
+--- 构造方法
+--- @return LinkedListDeque
+function LinkedListDeque.new()
+ local obj = {}
+ setmetatable(obj, LinkedListDeque)
+ obj._front = nil -- 头节点 front
+ obj._rear = nil -- 尾节点 rear
+ obj._size = 0 -- 双向队列的长度
+ return obj
+end
+
+--- 获取双向队列的长度
+--- @return number
+function LinkedListDeque:size()
+ return self._size
+end
+
+--- 判断双向队列是否为空
+--- @return boolean
+function LinkedListDeque:is_empty()
+ return self._size == 0
+end
+
+--- 入队操作
+--- @param num number 入队值
+--- @param is_front boolean 是否在队首入队
+function LinkedListDeque:push(num, is_front)
+ local node = DeListNode.new(num)
+
+ -- 若链表为空,则令 front 和 rear 都指向 node
+ if self:is_empty() then
+ self._front = node
+ self._rear = node
+ -- 队首入队操作
+ elseif is_front then
+ -- 将 node 添加至链表头部
+ self._front.prev = node
+ node.next = self._front
+ self._front = node -- 更新头节点
+ -- 队尾入队操作
+ else
+ -- 将 node 添加至链表尾部
+ self._rear.next = node
+ node.prev = self._rear
+ self._rear = node -- 更新尾节点
+ end
+ self._size = self._size + 1 -- 更新队列长度
+end
+
+--- 队首入队
+--- @param num number 入队值
+function LinkedListDeque:push_first(num)
+ self:push(num, true)
+end
+
+--- 队尾入队
+--- @param num number 入队值
+function LinkedListDeque:push_last(num)
+ self:push(num, false)
+end
+
+--- 出队操作
+--- @param is_front boolean 是否在队首出队
+--- @return number
+--- @raise 双向队列为空时抛出错误
+function LinkedListDeque:pop(is_front)
+ if self:is_empty() then
+ error("双向队列为空")
+ end
+
+ local val
+ -- 队首出队操作
+ if is_front then
+ val = self._front.val -- 暂存头节点值
+ -- 删除头节点
+ local fnext = self._front.next
+ if fnext ~= nil then
+ fnext.prev = nil
+ self._front.next = nil
+ end
+ self._front = fnext -- 更新头节点
+ -- 队尾出队操作
+ else
+ val = self._rear.val -- 暂存尾节点值
+ -- 删除尾节点
+ local rprev = self._rear.prev
+ if rprev ~= nil then
+ rprev.next = nil
+ self._rear.prev = nil
+ end
+ self._rear = rprev -- 更新尾节点
+ end
+ self._size = self._size - 1 -- 更新队列长度
+ return val
+end
+
+--- 队首出队
+--- @return number
+function LinkedListDeque:pop_first()
+ return self:pop(true)
+end
+
+--- 队尾出队
+--- @return number
+function LinkedListDeque:pop_last()
+ return self:pop(false)
+end
+
+--- 访问队首元素
+--- @return number
+function LinkedListDeque:peek_first()
+ if self:is_empty() then
+ error("双向队列为空")
+ end
+ return self._front.val
+end
+
+--- 访问队尾元素
+--- @return number
+function LinkedListDeque:peek_last()
+ if self:is_empty() then
+ error("双向队列为空")
+ end
+ return self._rear.val
+end
+
+--- 返回数组用于打印
+--- @return table
+function LinkedListDeque:to_array()
+ local node = self._front
+ local res = {}
+ for i = 1, self:size() do
+ res[i] = node.val
+ node = node.next
+ end
+ return res
+end
+
+-- Driver Code
+local function main()
+ -- 初始化双向队列
+ local deque = LinkedListDeque.new()
+ deque:push_last(3)
+ deque:push_last(2)
+ deque:push_last(5)
+ print("双向队列 deque = [" .. table.concat(deque:to_array(), ", ") .. "]")
+
+ -- 访问元素
+ local peek_first = deque:peek_first()
+ print("队首元素 peek_first = " .. tostring(peek_first))
+ local peek_last = deque:peek_last()
+ print("队尾元素 peek_last = " .. tostring(peek_last))
+
+ -- 元素入队
+ deque:push_last(4)
+ print("元素 4 队尾入队后 deque = [" .. table.concat(deque:to_array(), ", ") .. "]")
+ deque:push_first(1)
+ print("元素 1 队首入队后 deque = [" .. table.concat(deque:to_array(), ", ") .. "]")
+
+ -- 元素出队
+ local pop_last = deque:pop_last()
+ print(string.format("队尾出队元素 = %d ,队尾出队后 deque = [%s]", pop_last, table.concat(deque:to_array(), ", ")))
+ local pop_first = deque:pop_first()
+ print(string.format("队首出队元素 = %d ,队首出队后 deque = [%s]", pop_first, table.concat(deque:to_array(), ", ")))
+
+ -- 获取双向队列的长度
+ local size = deque:size()
+ print("双向队列长度 size = " .. tostring(size))
+
+ -- 判断双向队列是否为空
+ local is_empty = deque:is_empty()
+ print("双向队列是否为空 = " .. tostring(is_empty))
+end
+
+-- 执行主函数
+main()
diff --git a/codes/lua/chapter_stack_and_queue/linkedlist_queue.lua b/codes/lua/chapter_stack_and_queue/linkedlist_queue.lua
new file mode 100644
index 0000000000..a3f6b069f6
--- /dev/null
+++ b/codes/lua/chapter_stack_and_queue/linkedlist_queue.lua
@@ -0,0 +1,121 @@
+-- @script linked_queue.lua
+-- @date 2025-11-11
+-- @author fisheryv (yue.fisher2025@gdhfi.com)
+
+-- 添加模块搜索路径
+package.path = package.path .. ";codes/lua/modules/?.lua"
+
+local list_node = require("list_node")
+local ListNode = list_node.ListNode
+
+--- @class LinkedListQueue
+--- 基于链表实现的队列
+--- @field _front ListNode 队首节点
+--- @field _rear ListNode 队尾节点
+--- @field _size integer 队列长度
+local LinkedListQueue = {}
+LinkedListQueue.__index = LinkedListQueue
+
+--- 构造方法
+--- @return LinkedListQueue 新的队列实例
+function LinkedListQueue.new()
+ local instance = setmetatable({}, LinkedListQueue)
+ instance._front = nil -- 头节点 front
+ instance._rear = nil -- 尾节点 rear
+ instance._size = 0 -- 队列长度
+ return instance
+end
+
+--- 获取队列的长度
+--- @return integer
+function LinkedListQueue:size()
+ return self._size
+end
+
+--- 判断队列是否为空
+--- @return boolean
+function LinkedListQueue:is_empty()
+ return self._size == 0
+end
+
+--- 入队
+--- @param num number
+function LinkedListQueue:push(num)
+ -- 在尾节点后添加 num
+ local node = ListNode.new(num)
+ -- 如果队列为空,则令头、尾节点都指向该节点
+ if self._front == nil then
+ self._front = node
+ self._rear = node
+ -- 如果队列不为空,则将该节点添加到尾节点后
+ else
+ self._rear.next = node
+ self._rear = node
+ end
+ self._size = self._size + 1
+end
+
+--- 出队
+--- @return number
+function LinkedListQueue:pop()
+ local num = self:peek()
+ -- 删除头节点
+ self._front = self._front.next
+ self._size = self._size - 1
+ return num
+end
+
+--- 访问队首元素
+--- @return number
+function LinkedListQueue:peek()
+ if self:is_empty() then
+ error("队列为空")
+ end
+ return self._front.val
+end
+
+--- 转化为列表用于打印
+--- @return table
+function LinkedListQueue:to_list()
+ local queue = {}
+ local temp = self._front
+ while temp do
+ table.insert(queue, temp.val)
+ temp = temp.next
+ end
+ return queue
+end
+
+-- Driver Code
+local function main()
+ -- 初始化队列
+ local queue = LinkedListQueue.new()
+
+ -- 元素入队
+ queue:push(1)
+ queue:push(3)
+ queue:push(2)
+ queue:push(5)
+ queue:push(4)
+ print("队列 queue = [" .. table.concat(queue:to_list(), ", ") .. "]")
+
+ -- 访问队首元素
+ local peek = queue:peek()
+ print("队首元素 front = " .. tostring(peek))
+
+ -- 元素出队
+ local pop_front = queue:pop()
+ print("出队元素 pop = " .. tostring(pop_front))
+ print("出队后 queue = [" .. table.concat(queue:to_list(), ", ") .. "]")
+
+ -- 获取队列的长度
+ local size = queue:size()
+ print("队列长度 size = " .. tostring(size))
+
+ -- 判断队列是否为空
+ local is_empty = queue:is_empty()
+ print("队列是否为空 = " .. tostring(is_empty))
+end
+
+-- 执行主函数
+main()
diff --git a/codes/lua/chapter_stack_and_queue/linkedlist_stack.lua b/codes/lua/chapter_stack_and_queue/linkedlist_stack.lua
new file mode 100644
index 0000000000..cd026c6a39
--- /dev/null
+++ b/codes/lua/chapter_stack_and_queue/linkedlist_stack.lua
@@ -0,0 +1,113 @@
+-- @script linked_stack.lua
+-- @date 2025-11-11
+-- @author fisheryv (yue.fisher2025@gdhfi.com)
+
+
+-- 添加模块搜索路径
+package.path = package.path .. ";codes/lua/modules/?.lua"
+
+local list_node = require("list_node")
+local ListNode = list_node.ListNode
+
+--- @class LinkedListStack
+--- 基于链表实现的栈
+--- @field _peek ListNode 栈顶节点
+--- @field _size integer 栈的长度
+local LinkedListStack = {}
+LinkedListStack.__index = LinkedListStack
+
+--- 构造方法
+--- @return LinkedListStack 新的栈实例
+function LinkedListStack.new()
+ local obj = {}
+ setmetatable(obj, LinkedListStack)
+ obj._peek = nil -- 栈顶节点
+ obj._size = 0 -- 栈的长度
+ return obj
+end
+
+--- 获取栈的长度
+--- @return integer
+function LinkedListStack:size()
+ return self._size
+end
+
+--- 判断栈是否为空
+--- @return boolean
+function LinkedListStack:is_empty()
+ return self._size == 0
+end
+
+--- 入栈
+--- @param val number
+function LinkedListStack:push(val)
+ local node = ListNode.new(val)
+ node.next = self._peek
+ self._peek = node
+ self._size = self._size + 1
+end
+
+--- 出栈
+--- @return number
+function LinkedListStack:pop()
+ local num = self:peek()
+ self._peek = self._peek.next
+ self._size = self._size - 1
+ return num
+end
+
+--- 访问栈顶元素
+--- @return number 栈顶元素
+--- @raise 栈为空时抛出错误
+function LinkedListStack:peek()
+ if self:is_empty() then
+ error("栈为空")
+ end
+ return self._peek.val
+end
+
+---转化为列表用于打印
+---@return table
+function LinkedListStack:to_list()
+ local arr = {}
+ local node = self._peek
+ while node do
+ table.insert(arr, 1, node.val) -- 在头部插入,相当于 reverse
+ node = node.next
+ end
+ return arr
+end
+
+-- Driver Code
+local function main()
+ -- 初始化栈
+ local stack = LinkedListStack.new()
+
+ -- 元素入栈
+ stack:push(1)
+ stack:push(3)
+ stack:push(2)
+ stack:push(5)
+ stack:push(4)
+ print("栈 stack = [" .. table.concat(stack:to_list(), ", ") .. "]")
+
+ -- 访问栈顶元素
+ local peek = stack:peek()
+ print("栈顶元素 peek = " .. peek)
+
+ -- 元素出栈
+ local pop = stack:pop()
+ print("出栈元素 pop = " .. pop)
+ print("出栈后 stack = [" .. table.concat(stack:to_list(), ", ") .. "]")
+
+ -- 获取栈的长度
+ local size = stack:size()
+ print("栈的长度 size = " .. size)
+
+ -- 判断是否为空
+ local is_empty = stack:is_empty()
+ print("栈是否为空 = " .. tostring(is_empty))
+end
+
+-- 执行主函数
+main()
diff --git a/codes/lua/chapter_stack_and_queue/queue.lua b/codes/lua/chapter_stack_and_queue/queue.lua
new file mode 100644
index 0000000000..5ffedd627b
--- /dev/null
+++ b/codes/lua/chapter_stack_and_queue/queue.lua
@@ -0,0 +1,39 @@
+-- @script queue.lua
+-- @date 2025-11-11
+-- @author fisheryv (yue.fisher2025@gdhfi.com)
+
+
+-- Driver Code
+local function main()
+ -- 使用Lua表模拟队列数据结构
+ -- 初始化队列
+ local que = {}
+
+ -- 元素入队
+ table.insert(que, 1)
+ table.insert(que, 3)
+ table.insert(que, 2)
+ table.insert(que, 5)
+ table.insert(que, 4)
+ print("队列 que = [" .. table.concat(que, ", ") .. "]")
+
+ -- 访问队首元素
+ local front = que[1]
+ print("队首元素 front = " .. tostring(front))
+
+ -- 元素出队
+ local pop = #que == 0 and nil or table.remove(que, 1)
+ print("出队元素 pop = " .. tostring(pop))
+ print("出队后 que = [" .. table.concat(que, ", ") .. "]")
+
+ -- 获取队列长度
+ local size = #que
+ print("队列长度 size = " .. tostring(size))
+
+ -- 判断队列是否为空
+ local is_empty = #que == 0
+ print("队列是否为空 = " .. tostring(is_empty))
+end
+
+-- 执行主函数
+main()
diff --git a/codes/lua/chapter_stack_and_queue/stack.lua b/codes/lua/chapter_stack_and_queue/stack.lua
new file mode 100644
index 0000000000..7776337c1c
--- /dev/null
+++ b/codes/lua/chapter_stack_and_queue/stack.lua
@@ -0,0 +1,39 @@
+-- @script stack.lua
+-- @date 2025-11-11
+-- @author fisheryv (yue.fisher2025@gdhfi.com)
+
+
+-- Driver Code
+local function main()
+ -- 初始化栈
+ -- Lua 没有内置的栈类,可以把 table 当作栈来使用
+ local stack = {}
+
+ -- 元素入栈
+ table.insert(stack, 1)
+ table.insert(stack, 3)
+ table.insert(stack, 2)
+ table.insert(stack, 5)
+ table.insert(stack, 4)
+ print("栈 stack = [" .. table.concat(stack, ", ") .. "]")
+
+ -- 访问栈顶元素
+ local peek = stack[#stack]
+ print("栈顶元素 peek = " .. peek)
+
+ -- 元素出栈
+ local pop = table.remove(stack)
+ print("出栈元素 pop = " .. pop)
+ print("出栈后 stack = [" .. table.concat(stack, ", ") .. "]")
+
+ -- 获取栈的长度
+ local size = #stack
+ print("栈的长度 size = " .. size)
+
+ -- 判断是否为空
+ local is_empty = #stack == 0
+ print("栈是否为空 = " .. tostring(is_empty))
+end
+
+-- 执行主函数
+main()
diff --git a/codes/lua/chapter_tree/array_binary_tree.lua b/codes/lua/chapter_tree/array_binary_tree.lua
new file mode 100644
index 0000000000..470683f0c9
--- /dev/null
+++ b/codes/lua/chapter_tree/array_binary_tree.lua
@@ -0,0 +1,166 @@
+-- @script array_binary_tree.lua
+-- @date 2025-11-14
+-- @author fisheryv (yue.fisher2025@gdhfi.com)
+
+-- 添加模块搜索路径
+package.path = package.path .. ";codes/lua/modules/?.lua"
+
+local print_util = require("print_util")
+local tree_node = require("tree_node")
+local list_to_tree = tree_node.list_to_tree
+
+--- @class ArrayBinaryTree
+--- 数组表示下的二叉树类
+--- @field _tree number[] 二叉树的数组表示
+local ArrayBinaryTree = {}
+
+--- 构造方法
+--- @param arr number[] 二叉树数组表示
+--- @return ArrayBinaryTree
+function ArrayBinaryTree:new(arr)
+ local obj = {}
+ setmetatable(obj, self)
+ self.__index = self
+ obj._tree = {}
+ for i, v in ipairs(arr) do
+ obj._tree[i] = v
+ end
+ return obj
+end
+
+---列表容量
+---@return integer
+function ArrayBinaryTree:size()
+ return #self._tree
+end
+
+--- 获取索引为 i 节点的值
+--- @param i integer 节点索引
+--- @return number 节点值,若索引越界则返回 math.huge
+function ArrayBinaryTree:val(i)
+ -- 若索引越界,则返回 math.huge ,代表空位
+ if i < 1 or i > self:size() then
+ return math.huge
+ end
+ return self._tree[i]
+end
+
+--- 获取索引为 i 节点的左子节点的索引
+--- @param i integer 节点索引
+--- @return integer
+function ArrayBinaryTree:left(i)
+ return 2 * i
+end
+
+--- 获取索引为 i 节点的右子节点的索引
+--- @param i integer 节点索引
+--- @return integer
+function ArrayBinaryTree:right(i)
+ return 2 * i + 1
+end
+
+--- 获取索引为 i 节点的父节点的索引
+--- @param i integer 节点索引
+--- @return integer
+function ArrayBinaryTree:parent(i)
+ return math.floor(i / 2)
+end
+
+--- 层序遍历
+--- @return number[]
+function ArrayBinaryTree:level_order()
+ local res = {}
+ -- 直接遍历数组
+ for i = 1, self:size() do
+ if self:val(i) ~= math.huge then
+ table.insert(res, self:val(i))
+ end
+ end
+ return res
+end
+
+--- 深度优先遍历
+--- @param i integer 当前节点索引
+--- @param order string 遍历顺序:"pre"|"in"|"post"
+--- @param res table 结果数组
+function ArrayBinaryTree:_dfs(i, order, res)
+ if self:val(i) == math.huge then
+ return
+ end
+ -- 前序遍历
+ if order == "pre" then
+ table.insert(res, self:val(i))
+ end
+ self:_dfs(self:left(i), order, res)
+ -- 中序遍历
+ if order == "in" then
+ table.insert(res, self:val(i))
+ end
+ self:_dfs(self:right(i), order, res)
+ -- 后序遍历
+ if order == "post" then
+ table.insert(res, self:val(i))
+ end
+end
+
+--- 前序遍历
+--- @return table
+function ArrayBinaryTree:pre_order()
+ local res = {}
+ self:_dfs(1, "pre", res)
+ return res
+end
+
+--- 中序遍历
+--- @return table
+function ArrayBinaryTree:in_order()
+ local res = {}
+ self:_dfs(1, "in", res)
+ return res
+end
+
+--- 后序遍历
+--- @return table
+function ArrayBinaryTree:post_order()
+ local res = {}
+ self:_dfs(1, "post", res)
+ return res
+end
+
+-- Driver Code
+local function main()
+ -- 初始化二叉树
+ -- 注意:Lua中数组索引从1开始,用 math.huge 表示空位
+ local arr = { 1, 2, 3, 4, math.huge, 6, 7, 8, 9, math.huge, math.huge, 12, math.huge, math.huge, 15 }
+ local root = list_to_tree(arr)
+
+ print("\n初始化二叉树\n")
+ print("二叉树的数组表示:")
+ print(string.format("[%s]", table.concat(arr, ", ")))
+ print("二叉树的链表表示:")
+ print_util.print_tree(root, nil, false)
+
+ -- 数组表示下的二叉树类
+ local abt = ArrayBinaryTree:new(arr)
+
+ -- 访问节点
+ local i = 2 -- Lua索引从1开始,对应原Python代码中的索引1
+ local l, r, p = abt:left(i), abt:right(i), abt:parent(i)
+ print(string.format("\n当前节点的索引为 %d ,值为 %s", i, tostring(abt:val(i))))
+ print(string.format("其左子节点的索引为 %d ,值为 %s", l, tostring(abt:val(l))))
+ print(string.format("其右子节点的索引为 %d ,值为 %s", r, tostring(abt:val(r))))
+ print(string.format("其父节点的索引为 %d ,值为 %s", p, tostring(abt:val(p))))
+
+ -- 遍历树
+ local res = abt:level_order()
+ print("\n层序遍历为:[" .. table.concat(res, ", ") .. "]")
+ res = abt:pre_order()
+ print("前序遍历为:[" .. table.concat(res, ", ") .. "]")
+ res = abt:in_order()
+ print("中序遍历为:[" .. table.concat(res, ", ") .. "]")
+ res = abt:post_order()
+ print("后序遍历为:[" .. table.concat(res, ", ") .. "]")
+end
+
+-- 运行主函数
+main()
diff --git a/codes/lua/chapter_tree/avl_tree.lua b/codes/lua/chapter_tree/avl_tree.lua
new file mode 100644
index 0000000000..09bddcd28d
--- /dev/null
+++ b/codes/lua/chapter_tree/avl_tree.lua
@@ -0,0 +1,268 @@
+-- @script avl_tree.lua
+-- @date 2025-11-14
+-- @author fisheryv (yue.fisher2025@gdhfi.com)
+
+
+-- 添加模块搜索路径
+package.path = package.path .. ";codes/lua/modules/?.lua"
+
+local print_util = require("print_util")
+local tree_node = require("tree_node")
+local TreeNode = tree_node.TreeNode
+
+--- @class AVLTree
+--- AVL 树
+--- @field _root TreeNode|nil AVL 树的根节点
+local AVLTree = {}
+
+---AVLTree 构造函数
+---@return AVLTree
+function AVLTree:new()
+ local obj = {}
+ setmetatable(obj, self)
+ self.__index = self
+ obj._root = nil
+ return obj
+end
+
+--- 获取二叉树根节点
+--- @return TreeNode|nil
+function AVLTree:get_root()
+ return self._root
+end
+
+--- 获取节点高度
+--- @param node TreeNode|nil
+--- @return integer
+function AVLTree:height(node)
+ -- 空节点高度为 -1 ,叶节点高度为 0
+ if node ~= nil then
+ return node.height
+ end
+ return -1
+end
+
+--- 更新节点高度
+--- @param node TreeNode|nil
+function AVLTree:update_height(node)
+ if node == nil then
+ return
+ end
+ -- 节点高度等于最高子树高度 + 1
+ node.height = math.max(self:height(node.left), self:height(node.right)) + 1
+end
+
+--- 获取平衡因子
+--- @param node TreeNode|nil
+--- @return integer
+function AVLTree:balance_factor(node)
+ -- 空节点平衡因子为 0
+ if node == nil then
+ return 0
+ end
+ -- 节点平衡因子 = 左子树高度 - 右子树高度
+ return self:height(node.left) - self:height(node.right)
+end
+
+--- 右旋操作
+--- @param node TreeNode
+--- @return TreeNode
+function AVLTree:right_rotate(node)
+ local child = node.left
+ local grand_child = child.right
+ -- 以 child 为原点,将 node 向右旋转
+ child.right = node
+ node.left = grand_child
+ -- 更新节点高度
+ self:update_height(node)
+ self:update_height(child)
+ -- 返回旋转后子树的根节点
+ return child
+end
+
+--- 左旋操作
+--- @param node TreeNode
+--- @return TreeNode
+function AVLTree:left_rotate(node)
+ local child = node.right
+ local grand_child = child.left
+ -- 以 child 为原点,将 node 向左旋转
+ child.left = node
+ node.right = grand_child
+ -- 更新节点高度
+ self:update_height(node)
+ self:update_height(child)
+ -- 返回旋转后子树的根节点
+ return child
+end
+
+--- 执行旋转操作,使该子树重新恢复平衡
+--- @param node TreeNode
+--- @return TreeNode
+function AVLTree:rotate(node)
+ -- 获取节点 node 的平衡因子
+ local balance_factor = self:balance_factor(node)
+ -- 左偏树
+ if balance_factor > 1 then
+ if self:balance_factor(node.left) >= 0 then
+ -- 右旋
+ return self:right_rotate(node)
+ else
+ -- 先左旋后右旋
+ node.left = self:left_rotate(node.left)
+ return self:right_rotate(node)
+ end
+ -- 右偏树
+ elseif balance_factor < -1 then
+ if self:balance_factor(node.right) <= 0 then
+ -- 左旋
+ return self:left_rotate(node)
+ else
+ -- 先右旋后左旋
+ node.right = self:right_rotate(node.right)
+ return self:left_rotate(node)
+ end
+ end
+ -- 平衡树,无须旋转,直接返回
+ return node
+end
+
+--- 插入节点
+--- @param val integer
+function AVLTree:insert(val)
+ self._root = self:insert_helper(self._root, val)
+end
+
+--- 递归插入节点(辅助方法)
+--- @param node TreeNode|nil
+--- @param val integer
+--- @return TreeNode
+function AVLTree:insert_helper(node, val)
+ if node == nil then
+ return TreeNode.new(val)
+ end
+ -- 1. 查找插入位置并插入节点
+ if val < node.val then
+ node.left = self:insert_helper(node.left, val)
+ elseif val > node.val then
+ node.right = self:insert_helper(node.right, val)
+ else
+ -- 重复节点不插入,直接返回
+ return node
+ end
+ -- 更新节点高度
+ self:update_height(node)
+ -- 2. 执行旋转操作,使该子树重新恢复平衡
+ return self:rotate(node)
+end
+
+--- 删除节点
+--- @param val integer
+function AVLTree:remove(val)
+ self._root = self:remove_helper(self._root, val)
+end
+
+--- 递归删除节点(辅助方法)
+--- @param node TreeNode|nil
+--- @param val integer
+--- @return TreeNode|nil
+function AVLTree:remove_helper(node, val)
+ if node == nil then
+ return nil
+ end
+ -- 1. 查找节点并删除
+ if val < node.val then
+ node.left = self:remove_helper(node.left, val)
+ elseif val > node.val then
+ node.right = self:remove_helper(node.right, val)
+ else
+ if node.left == nil or node.right == nil then
+ local child = node.left or node.right
+ -- 子节点数量 = 0 ,直接删除 node 并返回
+ if child == nil then
+ return nil
+ -- 子节点数量 = 1 ,直接删除 node
+ else
+ node = child
+ end
+ else
+ -- 子节点数量 = 2 ,则将中序遍历的下个节点删除,并用该节点替换当前节点
+ local temp = node.right
+ while temp.left ~= nil do
+ temp = temp.left
+ end
+ node.right = self:remove_helper(node.right, temp.val)
+ node.val = temp.val
+ end
+ end
+ -- 更新节点高度
+ self:update_height(node)
+ -- 2. 执行旋转操作,使该子树重新恢复平衡
+ return self:rotate(node)
+end
+
+--- 查找节点
+--- @param val integer
+--- @return TreeNode|nil
+function AVLTree:search(val)
+ local cur = self._root
+ -- 循环查找,越过叶节点后跳出
+ while cur ~= nil do
+ -- 目标节点在 cur 的右子树中
+ if cur.val < val then
+ cur = cur.right
+ -- 目标节点在 cur 的左子树中
+ elseif cur.val > val then
+ cur = cur.left
+ -- 找到目标节点,跳出循环
+ else
+ break
+ end
+ end
+ -- 返回目标节点
+ return cur
+end
+
+-- Driver Code
+local function main()
+ local function test_insert(tree, val)
+ tree:insert(val)
+ print(string.format("\n插入节点 %d 后,AVL 树为", val))
+ print_util.print_tree(tree:get_root(), nil, false)
+ end
+
+ local function test_remove(tree, val)
+ tree:remove(val)
+ print(string.format("\n删除节点 %d 后,AVL 树为", val))
+ print_util.print_tree(tree:get_root(), nil, false)
+ end
+
+ -- 初始化空 AVL 树
+ local avl_tree = AVLTree:new()
+
+ -- 插入节点
+ -- 请关注插入节点后,AVL 树是如何保持平衡的
+ local test_values = { 1, 2, 3, 4, 5, 8, 7, 9, 10, 6 }
+ for _, val in ipairs(test_values) do
+ test_insert(avl_tree, val)
+ end
+
+ -- 插入重复节点
+ test_insert(avl_tree, 7)
+
+ -- 删除节点
+ -- 请关注删除节点后,AVL 树是如何保持平衡的
+ test_remove(avl_tree, 8) -- 删除度为 0 的节点
+ test_remove(avl_tree, 5) -- 删除度为 1 的节点
+ test_remove(avl_tree, 4) -- 删除度为 2 的节点
+
+ local result_node = avl_tree:search(7)
+ if result_node then
+ print(string.format("\n查找到的节点对象为 %s,节点值 = %d", tostring(result_node), result_node.val))
+ else
+ print("\n未找到节点 7")
+ end
+end
+
+-- 运行主函数
+main()
diff --git a/codes/lua/chapter_tree/binary_search_tree.lua b/codes/lua/chapter_tree/binary_search_tree.lua
new file mode 100644
index 0000000000..8d81b889d3
--- /dev/null
+++ b/codes/lua/chapter_tree/binary_search_tree.lua
@@ -0,0 +1,192 @@
+-- @script binary_search_tree.lua
+-- @date 2025-11-14
+-- @author fisheryv (yue.fisher2025@gdhfi.com)
+
+
+-- 添加模块搜索路径
+package.path = package.path .. ";codes/lua/modules/?.lua"
+
+local print_util = require("print_util")
+local tree_node = require("tree_node")
+local TreeNode = tree_node.TreeNode
+
+--- @class BinarySearchTree
+--- 二叉搜索树类
+--- @field _root TreeNode|nil 二叉搜索树的根节点
+local BinarySearchTree = {}
+BinarySearchTree.__index = BinarySearchTree
+
+--- 构造方法
+--- 初始化空树
+--- @return BinarySearchTree
+function BinarySearchTree.new()
+ local obj = {}
+ setmetatable(obj, BinarySearchTree)
+ obj._root = nil
+ return obj
+end
+
+--- 获取二叉树根节点
+--- @return TreeNode|nil
+function BinarySearchTree:get_root()
+ return self._root
+end
+
+--- 查找节点
+--- @param num number 要查找的值
+--- @return TreeNode|nil
+function BinarySearchTree:search(num)
+ local cur = self._root
+ -- 循环查找,越过叶节点后跳出
+ while cur ~= nil do
+ -- 目标节点在 cur 的右子树中
+ if cur.val < num then
+ cur = cur.right
+ -- 目标节点在 cur 的左子树中
+ elseif cur.val > num then
+ cur = cur.left
+ -- 找到目标节点,跳出循环
+ else
+ break
+ end
+ end
+ return cur
+end
+
+--- 插入节点
+--- @param num number 要插入的值
+function BinarySearchTree:insert(num)
+ -- 若树为空,则初始化根节点
+ if self._root == nil then
+ self._root = TreeNode.new(num)
+ return
+ end
+
+ -- 循环查找,越过叶节点后跳出
+ local cur = self._root
+ local pre = nil
+ while cur ~= nil do
+ -- 找到重复节点,直接返回
+ if cur.val == num then
+ return
+ end
+ pre = cur
+ -- 插入位置在 cur 的右子树中
+ if cur.val < num then
+ cur = cur.right
+ -- 插入位置在 cur 的左子树中
+ else
+ cur = cur.left
+ end
+ end
+
+ -- 插入节点
+ local node = TreeNode.new(num)
+ if pre.val < num then
+ pre.right = node
+ else
+ pre.left = node
+ end
+end
+
+---删除节点
+---@param num number 要删除的值
+function BinarySearchTree:remove(num)
+ -- 若树为空,直接提前返回
+ if self._root == nil then
+ return
+ end
+
+ -- 循环查找,越过叶节点后跳出
+ local cur = self._root
+ local pre = nil
+ while cur ~= nil do
+ -- 找到待删除节点,跳出循环
+ if cur.val == num then
+ break
+ end
+ pre = cur
+ -- 待删除节点在 cur 的右子树中
+ if cur.val < num then
+ cur = cur.right
+ -- 待删除节点在 cur 的左子树中
+ else
+ cur = cur.left
+ end
+ end
+
+ -- 若无待删除节点,则直接返回
+ if cur == nil then
+ return
+ end
+
+ -- 子节点数量 = 0 or 1
+ if cur.left == nil or cur.right == nil then
+ -- 当子节点数量 = 0 / 1 时,child = null / 该子节点
+ local child = cur.left or cur.right
+ -- 删除节点 cur
+ if cur ~= self._root then
+ if pre.left == cur then
+ pre.left = child
+ else
+ pre.right = child
+ end
+ else
+ -- 若删除节点为根节点,则重新指定根节点
+ self._root = child
+ end
+ -- 子节点数量 = 2
+ else
+ -- 获取中序遍历中 cur 的下一个节点
+ local tmp = cur.right
+ while tmp.left ~= nil do
+ tmp = tmp.left
+ end
+ -- 递归删除节点 tmp
+ self:remove(tmp.val)
+ -- 用 tmp 覆盖 cur
+ cur.val = tmp.val
+ end
+end
+
+-- Driver Code
+local function main()
+ -- 初始化二叉搜索树
+ local bst = BinarySearchTree.new()
+ local nums = { 8, 4, 12, 2, 6, 10, 14, 1, 3, 5, 7, 9, 11, 13, 15 }
+ -- 请注意,不同的插入顺序会生成不同的二叉树,该序列可以生成一个完美二叉树
+ for _, num in ipairs(nums) do
+ bst:insert(num)
+ end
+ print("\n初始化的二叉树为\n")
+ print_util.print_tree(bst:get_root(), nil, false)
+
+ -- 查找节点
+ local node = bst:search(7)
+ if node then
+ print("\n查找到的节点对象为: " .. tostring(node) .. ",节点值= " .. node.val)
+ else
+ print("\n未找到节点 7")
+ end
+
+ -- 插入节点
+ bst:insert(16)
+ print("\n插入节点 16 后,二叉树为\n")
+ print_util.print_tree(bst:get_root(), nil, false)
+
+ -- 删除节点
+ bst:remove(1)
+ print("\n删除节点 1 后,二叉树为\n")
+ print_util.print_tree(bst:get_root(), nil, false)
+
+ bst:remove(2)
+ print("\n删除节点 2 后,二叉树为\n")
+ print_util.print_tree(bst:get_root(), nil, false)
+
+ bst:remove(4)
+ print("\n删除节点 4 后,二叉树为\n")
+ print_util.print_tree(bst:get_root(), nil, false)
+end
+
+-- 运行主函数
+main()
diff --git a/codes/lua/chapter_tree/binary_tree.lua b/codes/lua/chapter_tree/binary_tree.lua
new file mode 100644
index 0000000000..b4ec87cba5
--- /dev/null
+++ b/codes/lua/chapter_tree/binary_tree.lua
@@ -0,0 +1,47 @@
+-- @script binary_tree.lua
+-- @date 2025-11-13
+-- @author fisheryv (yue.fisher2025@gdhfi.com)
+
+
+-- 添加模块搜索路径
+package.path = package.path .. ";codes/lua/modules/?.lua"
+
+local print_util = require("print_util")
+local tree_node = require("tree_node")
+local TreeNode = tree_node.TreeNode
+
+-- Driver Code
+local function main()
+ -- 初始化二叉树
+ -- 初始化节点
+ local n1 = TreeNode.new(1)
+ local n2 = TreeNode.new(2)
+ local n3 = TreeNode.new(3)
+ local n4 = TreeNode.new(4)
+ local n5 = TreeNode.new(5)
+
+ -- 构建节点之间的引用(指针)
+ n1.left = n2
+ n1.right = n3
+ n2.left = n4
+ n2.right = n5
+
+ print("\n初始化二叉树\n")
+ print_util.print_tree(n1, nil, false)
+
+ -- 插入与删除节点
+ local P = TreeNode.new(0)
+ -- 在 n1 -> n2 中间插入节点 P
+ n1.left = P
+ P.left = n2
+ print("\n插入节点 P 后\n")
+ print_util.print_tree(n1, nil, false)
+
+ -- 删除节点
+ n1.left = n2
+ print("\n删除节点 P 后\n")
+ print_util.print_tree(n1, nil, false)
+end
+
+-- 执行主函数
+main()
diff --git a/codes/lua/chapter_tree/binary_tree_bfs.lua b/codes/lua/chapter_tree/binary_tree_bfs.lua
new file mode 100644
index 0000000000..8f209a28e2
--- /dev/null
+++ b/codes/lua/chapter_tree/binary_tree_bfs.lua
@@ -0,0 +1,60 @@
+-- @script binary_tree_bfs.lua
+-- @date 2025-11-13
+-- @author fisheryv (yue.fisher2025@gdhfi.com)
+
+
+-- 添加模块搜索路径
+package.path = package.path .. ";codes/lua/modules/?.lua"
+
+local print_util = require("print_util")
+local tree_node = require("tree_node")
+local list_to_tree = tree_node.list_to_tree
+
+--- 层序遍历二叉树
+--- @param root TreeNode|nil 二叉树的根节点
+--- @return table
+local function level_order(root)
+ if not root then
+ return {}
+ end
+
+ -- 初始化队列,加入根节点
+ local queue = { root }
+ -- 初始化一个列表,用于保存遍历序列
+ local res = {}
+
+ while #queue > 0 do
+ -- 队列出队
+ local node = table.remove(queue, 1)
+ -- 保存节点值
+ table.insert(res, node.val)
+
+ -- 左子节点入队
+ if node.left then
+ table.insert(queue, node.left)
+ end
+
+ -- 右子节点入队
+ if node.right then
+ table.insert(queue, node.right)
+ end
+ end
+
+ return res
+end
+
+-- Driver Code
+local function main()
+ -- 初始化二叉树
+ -- 这里借助了一个从数组直接生成二叉树的函数
+ local root = list_to_tree({ 1, 2, 3, 4, 5, 6, 7 })
+ print("\n初始化二叉树\n")
+ print_util.print_tree(root, nil, false)
+
+ -- 层序遍历
+ local res = level_order(root)
+ print("\n层序遍历的节点打印序列 = [" .. table.concat(res, ", ") .. "]\n")
+end
+
+-- 执行主函数
+main()
diff --git a/codes/lua/chapter_tree/binary_tree_dfs.lua b/codes/lua/chapter_tree/binary_tree_dfs.lua
new file mode 100644
index 0000000000..e231c63247
--- /dev/null
+++ b/codes/lua/chapter_tree/binary_tree_dfs.lua
@@ -0,0 +1,77 @@
+-- @script binary_tree_dfs.lua
+-- @date 2025-11-13
+-- @author fisheryv (yue.fisher2025@gdhfi.com)
+
+
+-- 添加模块搜索路径
+package.path = package.path .. ";codes/lua/modules/?.lua"
+
+local print_util = require("print_util")
+local tree_node = require("tree_node")
+local list_to_tree = tree_node.list_to_tree
+
+-- 全局结果表,用于存储遍历结果
+local res = {}
+
+--- 前序遍历
+--- @param root TreeNode | nil 树的根节点
+local function pre_order(root)
+ if root == nil then
+ return
+ end
+ -- 访问优先级:根节点 -> 左子树 -> 右子树
+ table.insert(res, root.val)
+ pre_order(root.left)
+ pre_order(root.right)
+end
+
+--- 中序遍历
+--- @param root TreeNode | nil 树的根节点
+local function in_order(root)
+ if root == nil then
+ return
+ end
+ -- 访问优先级:左子树 -> 根节点 -> 右子树
+ in_order(root.left)
+ table.insert(res, root.val)
+ in_order(root.right)
+end
+
+--- 后序遍历
+--- @param root TreeNode | nil 树的根节点
+local function post_order(root)
+ if root == nil then
+ return
+ end
+ -- 访问优先级:左子树 -> 右子树 -> 根节点
+ post_order(root.left)
+ post_order(root.right)
+ table.insert(res, root.val)
+end
+
+-- Driver Code
+local function main()
+ -- 初始化二叉树
+ -- 这里借助了一个从数组直接生成二叉树的函数
+ local root = list_to_tree({ 1, 2, 3, 4, 5, 6, 7 })
+ print("\n初始化二叉树\n")
+ print_util.print_tree(root, nil, false)
+
+ -- 前序遍历
+ res = {}
+ pre_order(root)
+ print("\n前序遍历的节点打印序列 = [" .. table.concat(res, ", ") .. "]")
+
+ -- 中序遍历
+ res = {}
+ in_order(root)
+ print("\n中序遍历的节点打印序列 = [" .. table.concat(res, ", ") .. "]")
+
+ -- 后序遍历
+ res = {}
+ post_order(root)
+ print("\n后序遍历的节点打印序列 = [" .. table.concat(res, ", ") .. "]")
+end
+
+-- 执行主函数
+main()
diff --git a/codes/lua/modules/heapq.lua b/codes/lua/modules/heapq.lua
new file mode 100644
index 0000000000..b118d6273a
--- /dev/null
+++ b/codes/lua/modules/heapq.lua
@@ -0,0 +1,89 @@
+-- @module heapq
+-- @date 2025-11-14
+-- @author fisheryv (yue.fisher2025@gdhfi.com)
+
+local heapq = {}
+
+--- 上浮调整
+--- @param heap table 堆数组
+--- @param index integer 要调整的节点索引
+local function _sift_up(heap, index)
+ while index > 1 do
+ local parent = math.floor(index / 2)
+ if heap[parent] <= heap[index] then
+ break
+ end
+ heap[parent], heap[index] = heap[index], heap[parent]
+ index = parent
+ end
+end
+
+--- 下沉调整
+--- @param heap table 堆数组
+--- @param index integer 要调整的节点索引
+local function _sift_down(heap, index)
+ local n = #heap
+ while true do
+ local left = 2 * index
+ local right = 2 * index + 1
+ local smallest = index
+
+ if left <= n and heap[left] < heap[smallest] then
+ smallest = left
+ end
+
+ if right <= n and heap[right] < heap[smallest] then
+ smallest = right
+ end
+
+ if smallest == index then
+ break
+ end
+
+ heap[index], heap[smallest] = heap[smallest], heap[index]
+ index = smallest
+ end
+end
+
+--- 建堆操作
+--- @param heap 要构建为堆的数组
+function heapq.heapify(heap)
+ -- 从最后一个非叶子节点开始调整
+ for i = math.floor(#heap / 2), 1, -1 do
+ _sift_down(heap, i)
+ end
+end
+
+--- 元素入堆
+--- @function push
+--- @param heap table 堆数组
+--- @param val integer 要插入的值
+function heapq.heappush(heap, val)
+ -- 元素入堆
+ table.insert(heap, val)
+ -- 上浮调整
+ _sift_up(heap, #heap)
+end
+
+--- 堆顶元素出堆
+--- @param heap table 堆数组
+function heapq.heappop(heap)
+ flag = flag or 1
+ if #heap == 0 then
+ print("\n堆为空,无法出堆")
+ return
+ end
+
+ -- 交换堆顶和堆底元素
+ heap[1], heap[#heap] = heap[#heap], heap[1]
+ -- 堆顶元素出堆
+ local val = flag * table.remove(heap)
+ -- 下沉调整
+ if #heap > 0 then
+ _sift_down(heap, 1)
+ end
+
+ return val
+end
+
+return heapq
diff --git a/codes/lua/modules/list_node.lua b/codes/lua/modules/list_node.lua
new file mode 100644
index 0000000000..cd73336d2e
--- /dev/null
+++ b/codes/lua/modules/list_node.lua
@@ -0,0 +1,52 @@
+-- @classmod list_node
+-- @date 2025-11-10
+-- @author fisheryv (yue.fisher2025@gdhfi.com)
+
+--- @class ListNode
+--- 链表节点类
+--- @field val number 节点值
+--- @field next ListNode|nil 后继节点引用
+local ListNode = {}
+ListNode.__index = ListNode
+
+--- 构造函数
+--- @param val number 节点值
+--- @return ListNode 新节点实例
+function ListNode.new(val)
+ local obj = setmetatable({}, ListNode)
+ obj.val = val -- 节点值
+ obj.next = nil -- 后继节点引用
+ return obj
+end
+
+--- 将列表反序列化为链表
+--- @param arr table 整型数组
+--- @return ListNode|nil 链表头节点
+local function list_to_linked_list(arr)
+ local dum = ListNode.new(0)
+ local head = dum
+ for _, a in ipairs(arr) do
+ local node = ListNode.new(a)
+ head.next = node
+ head = head.next
+ end
+ return dum.next
+end
+
+--- 将链表序列化为列表
+--- @param head ListNode 链表头节点
+--- @return table 整型数组
+local function linked_list_to_list(head)
+ local arr = {}
+ while head do
+ table.insert(arr, head.val)
+ head = head.next
+ end
+ return arr
+end
+
+return {
+ ListNode = ListNode,
+ list_to_linked_list = list_to_linked_list,
+ linked_list_to_list = linked_list_to_list,
+}
diff --git a/codes/lua/modules/pair.lua b/codes/lua/modules/pair.lua
new file mode 100644
index 0000000000..7207dbd5a3
--- /dev/null
+++ b/codes/lua/modules/pair.lua
@@ -0,0 +1,24 @@
+-- @classmod pair
+-- @date 2025-11-12
+-- @author fisheryv (yue.fisher2025@gdhfi.com)
+
+--- @class Pair
+--- 键值对
+--- @field key integer 键
+--- @field val string 值
+local Pair = {}
+
+--- 构造函数
+--- @param key integer 键
+--- @param val string 值
+--- @return Pair
+function Pair:new(key, val)
+ local obj = {}
+ setmetatable(obj, self)
+ self.__index = self
+ obj.key = key
+ obj.val = val
+ return obj
+end
+
+return Pair
\ No newline at end of file
diff --git a/codes/lua/modules/print_util.lua b/codes/lua/modules/print_util.lua
new file mode 100644
index 0000000000..5ed2d12a14
--- /dev/null
+++ b/codes/lua/modules/print_util.lua
@@ -0,0 +1,114 @@
+-- @module print_util
+-- @date 2025-11-10
+-- @author fisheryv (yue.fisher2025@gdhfi.com)
+
+local print_util = {}
+
+-- 导入依赖
+package.path = package.path .. ";codes/lua/modules/?.lua"
+local tree_node = require("tree_node")
+local list_node = require("list_node")
+
+--- 打印矩阵
+--- @param mat table 二维矩阵表
+function print_util.print_matrix(mat)
+ local s = {}
+ for i, arr in ipairs(mat) do
+ s[i] = " [" .. table.concat(arr, ", ") .. "]"
+ end
+ print("[\n" .. table.concat(s, ",\n") .. "\n]")
+end
+
+--- 打印链表
+--- @param head table 链表头节点
+function print_util.print_linked_list(head)
+ local arr = list_node.linked_list_to_list(head)
+ local str_arr = {}
+ for i, v in ipairs(arr) do
+ str_arr[i] = tostring(v)
+ end
+ print(table.concat(str_arr, " -> "))
+end
+
+--- @class Trunk
+--- 树干类,用于打印二叉树
+--- @field prev Trunk|nil 前一个树干
+--- @field str string 树干字符串
+local Trunk = {}
+Trunk.__index = Trunk
+
+--- 构造函数
+--- @param prev table|nil 前一个树干
+--- @param str string 树干字符串
+--- @return Trunk 新树干实例
+function Trunk.new(prev, str)
+ local obj = {
+ prev = prev,
+ str = str
+ }
+ setmetatable(obj, Trunk)
+ return obj
+end
+
+--- 显示树干
+--- @param p table 树干实例
+local function show_trunks(p)
+ if not p then
+ return
+ end
+ show_trunks(p.prev)
+ io.write(p.str)
+end
+
+--- 打印二叉树
+--- This tree printer is borrowed from TECHIE DELIGHT
+--- @see https://www.techiedelight.com/c-program-print-binary-tree/
+--- @param root table|nil 二叉树根节点
+--- @param prev table|nil 前一个树干
+--- @param is_right boolean 是否为右子树
+function print_util.print_tree(root, prev, is_right)
+ if not root then
+ return
+ end
+
+ local prev_str = " "
+ local trunk = Trunk.new(prev, prev_str)
+ print_util.print_tree(root.right, trunk, true)
+
+ if not prev then
+ trunk.str = "———"
+ elseif is_right then
+ trunk.str = "/———"
+ prev_str = " |"
+ else
+ trunk.str = "\\———"
+ prev.str = prev_str
+ end
+
+ show_trunks(trunk)
+ print(" " .. tostring(root.val))
+ if prev then
+ prev.str = prev_str
+ end
+ trunk.str = " |"
+ print_util.print_tree(root.left, trunk, false)
+end
+
+--- 打印字典
+--- @param hmap table 字典表
+function print_util.print_dict(hmap)
+ for key, value in pairs(hmap) do
+ print(string.format("%s -> %s", tostring(key), tostring(value)))
+ end
+end
+
+--- 打印堆
+--- @param heap table 堆数组
+function print_util.print_heap(heap)
+ print("堆的数组表示:[" .. table.concat(heap, ", ") .. "]")
+ print("堆的树状表示:")
+ local root = tree_node.list_to_tree(heap)
+ print_util.print_tree(root, nil, false)
+end
+
+return print_util
diff --git a/codes/lua/modules/tree_node.lua b/codes/lua/modules/tree_node.lua
new file mode 100644
index 0000000000..4638bd75cd
--- /dev/null
+++ b/codes/lua/modules/tree_node.lua
@@ -0,0 +1,98 @@
+-- @classmod tree_node
+-- @date 2025-11-10
+-- @author fisheryv (yue.fisher2025@gdhfi.com)
+
+--- @class TreeNode
+--- 二叉树节点类
+--- @field val number 节点值
+--- @field height number 节点高度
+--- @field left TreeNode|nil 左子节点引用
+--- @field right TreeNode|nil 右子节点引用
+local TreeNode = {}
+TreeNode.__index = TreeNode
+
+--- 构造函数
+--- @param val number 节点值
+--- @return TreeNode 新节点实例
+function TreeNode.new(val)
+ local obj = {}
+ setmetatable(obj, TreeNode)
+ obj.val = val or 0 -- 节点值
+ obj.height = 0 -- 节点高度
+ obj.left = nil -- 左子节点引用
+ obj.right = nil -- 右子节点引用
+ return obj
+end
+
+-- 序列化编码规则请参考:
+-- https://www.hello-algo.com/chapter_tree/array_representation_of_tree/
+-- 二叉树的数组表示:
+-- [1, 2, 3, 4, nil, 6, 7, 8, 9, nil, nil, 12, nil, nil, 15]
+-- 二叉树的链表表示:
+-- /——— 15
+-- /——— 7
+-- /——— 3
+-- | \——— 6
+-- | \——— 12
+-- ——— 1
+-- \——— 2
+-- | /——— 9
+-- \——— 4
+-- \——— 8
+
+--- 将列表反序列化为二叉树:递归
+--- @param arr table 输入数组
+--- @param i integer 当前索引(从1开始)
+--- @return TreeNode|nil 二叉树根节点
+local function list_to_tree_dfs(arr, i)
+ -- 如果索引超出数组长度,或者对应的元素为nil|math.huge,则返回nil
+ if i < 1 or i > #arr or arr[i] == nil or arr[i] == math.huge then
+ return nil
+ end
+ -- 构建当前节点
+ local root = TreeNode.new(arr[i])
+ -- 递归构建左右子树
+ root.left = list_to_tree_dfs(arr, 2 * i)
+ root.right = list_to_tree_dfs(arr, 2 * i + 1)
+ return root
+end
+
+--- 将列表反序列化为二叉树
+--- @param arr table 输入数组
+--- @return TreeNode|nil 二叉树根节点
+local function list_to_tree(arr)
+ return list_to_tree_dfs(arr, 1)
+end
+
+--- 将二叉树序列化为列表:递归
+--- @param root TreeNode 二叉树根节点
+--- @param i integer 当前索引
+--- @param res table 结果数组
+local function tree_to_list_dfs(root, i, res)
+ if root == nil then
+ return
+ end
+ if i > #res then
+ for j = #res + 1, i do
+ res[j] = nil
+ end
+ end
+ res[i] = root.val
+ tree_to_list_dfs(root.left, 2 * i, res)
+ tree_to_list_dfs(root.right, 2 * i + 1, res)
+end
+
+--- 将二叉树序列化为列表
+--- @param root TreeNode 二叉树根节点
+--- @return table 序列化后的数组
+local function tree_to_list(root)
+ local res = {}
+ tree_to_list_dfs(root, 1, res)
+ return res
+end
+
+return {
+ TreeNode = TreeNode,
+ list_to_tree = list_to_tree,
+ tree_to_list = tree_to_list,
+}
diff --git a/codes/lua/modules/vertex.lua b/codes/lua/modules/vertex.lua
new file mode 100644
index 0000000000..2e2aeb663b
--- /dev/null
+++ b/codes/lua/modules/vertex.lua
@@ -0,0 +1,46 @@
+-- @classmod vertex
+-- @date 2025-11-15
+-- @author fisheryv (yue.fisher2025@gdhfi.com)
+
+--- @class Vertex
+--- 顶点类
+--- @field val integer 顶点值
+local Vertex = {}
+Vertex.__index = Vertex
+
+--- 构造函数
+--- @param val integer 顶点值
+--- @return Vertex 新顶点实例
+function Vertex.new(val)
+ local obj = setmetatable({}, Vertex)
+ obj.val = val
+ return obj
+end
+
+--- 输入值列表,返回顶点列表
+--- @param vals table 值列表
+--- @return table 顶点列表
+local function vals_to_vets(vals)
+ local vets = {}
+ for _, val in ipairs(vals) do
+ table.insert(vets, Vertex.new(val))
+ end
+ return vets
+end
+
+--- 输入顶点列表,返回值列表
+--- @param vets table 顶点列表
+--- @return table 值列表
+local function vets_to_vals(vets)
+ local vals = {}
+ for _, vet in ipairs(vets) do
+ table.insert(vals, vet.val)
+ end
+ return vals
+end
+
+return {
+ Vertex = Vertex,
+ vals_to_vets = vals_to_vets,
+ vets_to_vals = vets_to_vals
+}
diff --git a/docs/chapter_appendix/installation.md b/docs/chapter_appendix/installation.md
index b0722afcda..aa97f53ea5 100644
--- a/docs/chapter_appendix/installation.md
+++ b/docs/chapter_appendix/installation.md
@@ -66,3 +66,8 @@ VS Code 拥有强大的扩展包生态系统,支持大多数编程语言的运
1. 下载并安装 [Rust](https://www.rust-lang.org/tools/install) 。
2. 在 VS Code 的插件市场中搜索 `rust` ,安装 [rust-analyzer](https://marketplace.visualstudio.com/items?itemName=rust-lang.rust-analyzer) 。
+
+### Lua 环境
+
+1. 下载并安装 [Lua](https://www.lua.org/download.html) 。
+2. 在 VS Code 的插件市场中搜索 `lua` ,安装 [Lua](https://marketplace.visualstudio.com/items?itemName=sumneko.lua) 。
\ No newline at end of file
diff --git a/docs/chapter_array_and_linkedlist/array.md b/docs/chapter_array_and_linkedlist/array.md
index 25b1a78a3f..7f096237e3 100755
--- a/docs/chapter_array_and_linkedlist/array.md
+++ b/docs/chapter_array_and_linkedlist/array.md
@@ -126,6 +126,14 @@
nums = [1, 3, 2, 5, 4]
```
+=== "Lua"
+
+ ```lua title="array.lua"
+ --- 初始化数组
+ local arr = {0, 0, 0, 0, 0}
+ local nums = {1, 3, 2, 5, 4}
+ ```
+
??? pythontutor "可视化运行"
https://pythontutor.com/render.html#code=%23%20%E5%88%9D%E5%A7%8B%E5%8C%96%E6%95%B0%E7%BB%84%0Aarr%20%3D%20%5B0%5D%20*%205%20%20%23%20%5B%200,%200,%200,%200,%200%20%5D%0Anums%20%3D%20%5B1,%203,%202,%205,%204%5D&cumulative=false&curInstr=0&heapPrimitives=nevernest&mode=display&origin=opt-frontend.js&py=311&rawInputLstJSON=%5B%5D&textReferences=false
diff --git a/docs/chapter_array_and_linkedlist/linked_list.md b/docs/chapter_array_and_linkedlist/linked_list.md
index 910e220333..6a1de4c2a2 100755
--- a/docs/chapter_array_and_linkedlist/linked_list.md
+++ b/docs/chapter_array_and_linkedlist/linked_list.md
@@ -185,6 +185,27 @@
end
```
+=== "Lua"
+
+ ```lua title=""
+ --- @class ListNode
+ --- 链表节点类
+ --- @field val interger 节点值
+ --- @field next ListNode|nil 指向后继节点引用
+ local ListNode = {}
+ ListNode.__index = ListNode
+
+ --- 构造函数
+ --- @param val interger 节点值
+ --- @return ListNode 新节点实例
+ function ListNode.new(val)
+ local obj = setmetatable({}, ListNode)
+ obj.val = val -- 节点值
+ obj.next = nil -- 指向后继节点引用
+ return obj
+ end
+ ```
+
## 链表常用操作
### 初始化链表
@@ -413,6 +434,24 @@
n3.next = n4
```
+=== "Lua"
+
+ ```lua title="linked_list.lua"
+ -- 初始化链表 1 -> 3 -> 2 -> 5 -> 4
+ -- 初始化各个节点
+ local n0 = ListNode.new(1)
+ local n1 = ListNode.new(3)
+ local n2 = ListNode.new(2)
+ local n3 = ListNode.new(5)
+ local n4 = ListNode.new(4)
+
+ -- 构建节点之间的引用
+ n0.next = n1
+ n1.next = n2
+ n2.next = n3
+ n3.next = n4
+ ```
+
??? pythontutor "可视化运行"
https://pythontutor.com/render.html#code=class%20ListNode%3A%0A%20%20%20%20%22%22%22%E9%93%BE%E8%A1%A8%E8%8A%82%E7%82%B9%E7%B1%BB%22%22%22%0A%20%20%20%20def%20__init__%28self,%20val%3A%20int%29%3A%0A%20%20%20%20%20%20%20%20self.val%3A%20int%20%3D%20val%20%20%23%20%E8%8A%82%E7%82%B9%E5%80%BC%0A%20%20%20%20%20%20%20%20self.next%3A%20ListNode%20%7C%20None%20%3D%20None%20%20%23%20%E5%90%8E%E7%BB%A7%E8%8A%82%E7%82%B9%E5%BC%95%E7%94%A8%0A%0A%22%22%22Driver%20Code%22%22%22%0Aif%20__name__%20%3D%3D%20%22__main__%22%3A%0A%20%20%20%20%23%20%E5%88%9D%E5%A7%8B%E5%8C%96%E9%93%BE%E8%A1%A8%201%20-%3E%203%20-%3E%202%20-%3E%205%20-%3E%204%0A%20%20%20%20%23%20%E5%88%9D%E5%A7%8B%E5%8C%96%E5%90%84%E4%B8%AA%E8%8A%82%E7%82%B9%0A%20%20%20%20n0%20%3D%20ListNode%281%29%0A%20%20%20%20n1%20%3D%20ListNode%283%29%0A%20%20%20%20n2%20%3D%20ListNode%282%29%0A%20%20%20%20n3%20%3D%20ListNode%285%29%0A%20%20%20%20n4%20%3D%20ListNode%284%29%0A%20%20%20%20%23%20%E6%9E%84%E5%BB%BA%E8%8A%82%E7%82%B9%E4%B9%8B%E9%97%B4%E7%9A%84%E5%BC%95%E7%94%A8%0A%20%20%20%20n0.next%20%3D%20n1%0A%20%20%20%20n1.next%20%3D%20n2%0A%20%20%20%20n2.next%20%3D%20n3%0A%20%20%20%20n3.next%20%3D%20n4&cumulative=false&curInstr=3&heapPrimitives=nevernest&mode=display&origin=opt-frontend.js&py=311&rawInputLstJSON=%5B%5D&textReferences=false
@@ -680,6 +719,29 @@
end
```
+=== "Lua"
+
+ ```lua title=""
+ --- @class ListNode
+ --- 双向链表节点类
+ --- @field val interger 节点值
+ --- @field next ListNode|nil 指向后继节点的引用
+ --- @field prev ListNode|nil 指向前驱节点的引用
+ local ListNode = {}
+ ListNode.__index = ListNode
+
+ --- 创建新的双向链表节点
+ --- @param val interger 节点值
+ --- @return ListNode 新的双向链表节点实例
+ function ListNode.new(val)
+ local obj = setmetatable({}, ListNode)
+ obj.val = val -- 节点值
+ obj.next = nil -- 指向后继节点的引用
+ obj.prev = nil -- 指向前驱节点的引用
+ return obj
+ end
+ ```
+

## 链表典型应用
diff --git a/docs/chapter_array_and_linkedlist/list.md b/docs/chapter_array_and_linkedlist/list.md
index ded6505eb0..7f59250e7e 100755
--- a/docs/chapter_array_and_linkedlist/list.md
+++ b/docs/chapter_array_and_linkedlist/list.md
@@ -147,6 +147,14 @@
nums = [1, 3, 2, 5, 4]
```
+=== "Lua"
+
+ ```lua title="list.lua"
+ -- 初始化列表
+ local nums1 = {} -- 无初始值
+ local nums = {1, 3, 2, 5, 4} -- 有初始值
+ ```
+
??? pythontutor "可视化运行"
https://pythontutor.com/render.html#code=%22%22%22Driver%20Code%22%22%22%0Aif%20__name__%20%3D%3D%20%22__main__%22%3A%0A%20%20%20%20%23%20%E5%88%9D%E5%A7%8B%E5%8C%96%E5%88%97%E8%A1%A8%0A%20%20%20%20%23%20%E6%97%A0%E5%88%9D%E5%A7%8B%E5%80%BC%0A%20%20%20%20nums1%20%3D%20%5B%5D%0A%20%20%20%20%23%20%E6%9C%89%E5%88%9D%E5%A7%8B%E5%80%BC%0A%20%20%20%20nums%20%3D%20%5B1,%203,%202,%205,%204%5D&cumulative=false&curInstr=4&heapPrimitives=nevernest&mode=display&origin=opt-frontend.js&py=311&rawInputLstJSON=%5B%5D&textReferences=false
@@ -278,6 +286,17 @@
nums[1] = 0 # 将索引 1 处的元素更新为 0
```
+=== "Lua"
+
+ ```lua title="list.lua"
+ -- 访问元素
+ -- 注意:Lua 索引从 1 开始
+ local num = nums[2] -- 访问索引 2 处的元素(对应其他语言的索引 1)
+
+ --- 更新元素
+ nums[2] = 0 -- 将索引 2 处的元素更新为 0
+ ```
+
??? pythontutor "可视化运行"
https://pythontutor.com/render.html#code=%22%22%22Driver%20Code%22%22%22%0Aif%20__name__%20%3D%3D%20%22__main__%22%3A%0A%20%20%20%20%23%20%E5%88%9D%E5%A7%8B%E5%8C%96%E5%88%97%E8%A1%A8%0A%20%20%20%20nums%20%3D%20%5B1,%203,%202,%205,%204%5D%0A%0A%20%20%20%20%23%20%E8%AE%BF%E9%97%AE%E5%85%83%E7%B4%A0%0A%20%20%20%20num%20%3D%20nums%5B1%5D%20%20%23%20%E8%AE%BF%E9%97%AE%E7%B4%A2%E5%BC%95%201%20%E5%A4%84%E7%9A%84%E5%85%83%E7%B4%A0%0A%0A%20%20%20%20%23%20%E6%9B%B4%E6%96%B0%E5%85%83%E7%B4%A0%0A%20%20%20%20nums%5B1%5D%20%3D%200%20%20%20%20%23%20%E5%B0%86%E7%B4%A2%E5%BC%95%201%20%E5%A4%84%E7%9A%84%E5%85%83%E7%B4%A0%E6%9B%B4%E6%96%B0%E4%B8%BA%200&cumulative=false&curInstr=3&heapPrimitives=nevernest&mode=display&origin=opt-frontend.js&py=311&rawInputLstJSON=%5B%5D&textReferences=false
@@ -532,6 +551,27 @@
nums.delete_at(3) # 删除索引 3 处的元素
```
+=== "Lua"
+
+ ```lua title="list.lua"
+ -- 清空列表
+ nums = {}
+
+ -- 在尾部添加元素
+ table.insert(nums, 1)
+ table.insert(nums, 3)
+ table.insert(nums, 2)
+ table.insert(nums, 5)
+ table.insert(nums, 4)
+
+ -- 在中间插入元素
+ -- 注意:Lua 索引从 1 开始
+ table.insert(nums, 4, 6) -- 在索引 4 处插入数字 6
+
+ -- 删除元素
+ table.remove(nums, 4) -- 删除索引 4 处的元素
+ ```
+
??? pythontutor "可视化运行"
https://pythontutor.com/render.html#code=%22%22%22Driver%20Code%22%22%22%0Aif%20__name__%20%3D%3D%20%22__main__%22%3A%0A%20%20%20%20%23%20%E6%9C%89%E5%88%9D%E5%A7%8B%E5%80%BC%0A%20%20%20%20nums%20%3D%20%5B1,%203,%202,%205,%204%5D%0A%20%20%20%20%0A%20%20%20%20%23%20%E6%B8%85%E7%A9%BA%E5%88%97%E8%A1%A8%0A%20%20%20%20nums.clear%28%29%0A%20%20%20%20%0A%20%20%20%20%23%20%E5%9C%A8%E5%B0%BE%E9%83%A8%E6%B7%BB%E5%8A%A0%E5%85%83%E7%B4%A0%0A%20%20%20%20nums.append%281%29%0A%20%20%20%20nums.append%283%29%0A%20%20%20%20nums.append%282%29%0A%20%20%20%20nums.append%285%29%0A%20%20%20%20nums.append%284%29%0A%20%20%20%20%0A%20%20%20%20%23%20%E5%9C%A8%E4%B8%AD%E9%97%B4%E6%8F%92%E5%85%A5%E5%85%83%E7%B4%A0%0A%20%20%20%20nums.insert%283,%206%29%20%20%23%20%E5%9C%A8%E7%B4%A2%E5%BC%95%203%20%E5%A4%84%E6%8F%92%E5%85%A5%E6%95%B0%E5%AD%97%206%0A%20%20%20%20%0A%20%20%20%20%23%20%E5%88%A0%E9%99%A4%E5%85%83%E7%B4%A0%0A%20%20%20%20nums.pop%283%29%20%20%20%20%20%20%20%20%23%20%E5%88%A0%E9%99%A4%E7%B4%A2%E5%BC%95%203%20%E5%A4%84%E7%9A%84%E5%85%83%E7%B4%A0&cumulative=false&curInstr=3&heapPrimitives=nevernest&mode=display&origin=opt-frontend.js&py=311&rawInputLstJSON=%5B%5D&textReferences=false
@@ -733,6 +773,21 @@
end
```
+=== "Lua"
+
+ ```lua title="list.lua"
+ -- 通过索引遍历列表
+ local count = 0
+ for i = 1, #nums do
+ count = count + nums[i]
+ end
+
+ -- 直接遍历列表元素
+ for _, num in ipairs(nums) do
+ count = count + num
+ end
+ ```
+
??? pythontutor "可视化运行"
https://pythontutor.com/render.html#code=%22%22%22Driver%20Code%22%22%22%0Aif%20__name__%20%3D%3D%20%22__main__%22%3A%0A%20%20%20%20%23%20%E5%88%9D%E5%A7%8B%E5%8C%96%E5%88%97%E8%A1%A8%0A%20%20%20%20nums%20%3D%20%5B1,%203,%202,%205,%204%5D%0A%20%20%20%20%0A%20%20%20%20%23%20%E9%80%9A%E8%BF%87%E7%B4%A2%E5%BC%95%E9%81%8D%E5%8E%86%E5%88%97%E8%A1%A8%0A%20%20%20%20count%20%3D%200%0A%20%20%20%20for%20i%20in%20range%28len%28nums%29%29%3A%0A%20%20%20%20%20%20%20%20count%20%2B%3D%20nums%5Bi%5D%0A%0A%20%20%20%20%23%20%E7%9B%B4%E6%8E%A5%E9%81%8D%E5%8E%86%E5%88%97%E8%A1%A8%E5%85%83%E7%B4%A0%0A%20%20%20%20for%20num%20in%20nums%3A%0A%20%20%20%20%20%20%20%20count%20%2B%3D%20num&cumulative=false&curInstr=3&heapPrimitives=nevernest&mode=display&origin=opt-frontend.js&py=311&rawInputLstJSON=%5B%5D&textReferences=false
@@ -844,6 +899,17 @@
nums += nums1
```
+=== "Lua"
+
+ ```lua title="list.lua"
+ -- 拼接两个列表
+ local nums1 = {6, 8, 7, 10, 9}
+ -- 将列表 nums1 拼接到 nums 之后
+ for _, v in ipairs(nums1) do
+ table.insert(nums, v)
+ end
+ ```
+
??? pythontutor "可视化运行"
https://pythontutor.com/render.html#code=%22%22%22Driver%20Code%22%22%22%0Aif%20__name__%20%3D%3D%20%22__main__%22%3A%0A%20%20%20%20%23%20%E5%88%9D%E5%A7%8B%E5%8C%96%E5%88%97%E8%A1%A8%0A%20%20%20%20nums%20%3D%20%5B1,%203,%202,%205,%204%5D%0A%20%20%20%20%0A%20%20%20%20%23%20%E6%8B%BC%E6%8E%A5%E4%B8%A4%E4%B8%AA%E5%88%97%E8%A1%A8%0A%20%20%20%20nums1%20%3D%20%5B6,%208,%207,%2010,%209%5D%0A%20%20%20%20nums%20%2B%3D%20nums1%20%20%23%20%E5%B0%86%E5%88%97%E8%A1%A8%20nums1%20%E6%8B%BC%E6%8E%A5%E5%88%B0%20nums%20%E4%B9%8B%E5%90%8E&cumulative=false&curInstr=3&heapPrimitives=nevernest&mode=display&origin=opt-frontend.js&py=311&rawInputLstJSON=%5B%5D&textReferences=false
@@ -942,6 +1008,13 @@
nums = nums.sort { |a, b| a <=> b } # 排序后,列表元素从小到大排列
```
+=== "Lua"
+
+ ```lua title="list.lua"
+ -- 排序列表
+ table.sort(nums) -- 排序后,列表元素从小到大排列
+ ```
+
??? pythontutor "可视化运行"
https://pythontutor.com/render.html#code=%22%22%22Driver%20Code%22%22%22%0Aif%20__name__%20%3D%3D%20%22__main__%22%3A%0A%20%20%20%20%23%20%E5%88%9D%E5%A7%8B%E5%8C%96%E5%88%97%E8%A1%A8%0A%20%20%20%20nums%20%3D%20%5B1,%203,%202,%205,%204%5D%0A%20%20%20%20%0A%20%20%20%20%23%20%E6%8E%92%E5%BA%8F%E5%88%97%E8%A1%A8%0A%20%20%20%20nums.sort%28%29%20%20%23%20%E6%8E%92%E5%BA%8F%E5%90%8E%EF%BC%8C%E5%88%97%E8%A1%A8%E5%85%83%E7%B4%A0%E4%BB%8E%E5%B0%8F%E5%88%B0%E5%A4%A7%E6%8E%92%E5%88%97&cumulative=false&curInstr=3&heapPrimitives=nevernest&mode=display&origin=opt-frontend.js&py=311&rawInputLstJSON=%5B%5D&textReferences=false
diff --git a/docs/chapter_backtracking/backtracking_algorithm.md b/docs/chapter_backtracking/backtracking_algorithm.md
index 3955ded731..fdf3a12ebd 100644
--- a/docs/chapter_backtracking/backtracking_algorithm.md
+++ b/docs/chapter_backtracking/backtracking_algorithm.md
@@ -429,6 +429,34 @@
end
```
+=== "Lua"
+
+ ```lua title=""
+ --- 回溯算法框架
+ local function backtrack(state, choices, res)
+ -- 检查是否为解
+ if is_solution(state) then
+ -- 记录解
+ record_solution(state, res)
+ -- 不再继续搜索
+ return
+ end
+
+ -- 遍历所有选择
+ for _, choice in ipairs(choices) do
+ -- 剪枝:检查选择是否合法
+ if is_valid(state, choice) then
+ -- 尝试:做出选择,更新状态
+ make_choice(state, choice)
+ -- 进行下一轮选择
+ backtrack(state, { choice.left, choice.right }, res)
+ -- 回退:撤销选择,恢复到之前的状态
+ undo_choice(state, choice)
+ end
+ end
+ end
+ ```
+
接下来,我们基于框架代码来解决例题三。状态 `state` 为节点遍历路径,选择 `choices` 为当前节点的左子节点和右子节点,结果 `res` 是路径列表:
```src
diff --git a/docs/chapter_computational_complexity/space_complexity.md b/docs/chapter_computational_complexity/space_complexity.md
index 13b48eb280..767900194c 100755
--- a/docs/chapter_computational_complexity/space_complexity.md
+++ b/docs/chapter_computational_complexity/space_complexity.md
@@ -361,6 +361,46 @@
end
```
+=== "Lua"
+
+ ```lua title=""
+ --- @class Node
+ --- 节点类
+ --- @field val number 节点值
+ --- @field next Node|nil 指向下一节点的引用
+ local Node = {}
+ Node.__index = Node
+
+ --- 构造函数
+ --- @param x number 节点值
+ --- @return Node
+ function Node.new(x)
+ local obj = {}
+ setmetatable(obj, Node)
+ obj.val = x -- 节点值
+ obj.next = nil -- 指向下一节点的引用
+ return obj
+ end
+
+ --- 示例函数
+ --- @return number
+ local function exampleFunction()
+ -- 执行某些操作...
+ return 0
+ end
+
+ --- 算法函数
+ --- @param n any 输入数据
+ --- @return number 输出数据
+ local function algorithm(n)
+ local A = 0 -- 暂存数据(常量,一般用大写字母表示)
+ local b = 0 -- 暂存数据(变量)
+ local node = Node.new(0) -- 暂存数据(对象)
+ local c = exampleFunction() -- 栈帧空间(调用函数)
+ return A + b + c -- 输出数据
+ end
+ ```
+
## 推算方法
空间复杂度的推算方法与时间复杂度大致相同,只需将统计对象从“操作数量”转为“使用空间大小”。
@@ -523,6 +563,20 @@
end
```
+=== "Lua"
+
+ ```lua title=""
+ local function algorithm(n)
+ local a = 0 -- O(1)
+ -- Lua 中没有专门的数组类型,而是使用 table 实现数组/列表的功能。
+ -- Lua 的 table 是动态的,无法指定大小,也没有 Python 的列表乘法,需要一个一个填入数据
+ local b = {} -- O(1)
+ if n > 10 then
+ local nums = {} -- O(10)
+ end
+ end
+ ```
+
**在递归函数中,需要注意统计栈帧空间**。观察以下代码:
=== "Python"
@@ -795,6 +849,33 @@
end
```
+=== "Lua"
+
+ ```lua title=""
+ --- 执行某些操作
+ --- @return integer
+ local function func()
+ return 0
+ end
+
+ --- 循环的空间复杂度为 O(1)
+ --- @param n integer
+ local function loop(n)
+ for i = 1, n do
+ func()
+ end
+ end
+
+ --- 递归的空间复杂度为 O(n)
+ --- @param n integer
+ local function recur(n)
+ if n == 1 then
+ return
+ end
+ return recur(n - 1)
+ end
+ ```
+
函数 `loop()` 和 `recur()` 的时间复杂度都为 $O(n)$ ,但空间复杂度不同。
- 函数 `loop()` 在循环中调用了 $n$ 次 `function()` ,每轮中的 `function()` 都返回并释放了栈帧空间,因此空间复杂度仍为 $O(1)$ 。
diff --git a/docs/chapter_computational_complexity/time_complexity.md b/docs/chapter_computational_complexity/time_complexity.md
index 7576382798..cd0dc71ef1 100755
--- a/docs/chapter_computational_complexity/time_complexity.md
+++ b/docs/chapter_computational_complexity/time_complexity.md
@@ -201,6 +201,22 @@
end
```
+=== "Lua"
+
+ ```lua title=""
+ --- 在某运行平台下
+ function algorithm(n)
+ local a = 2 -- 1 ns
+ a = a + 1 -- 1 ns
+ a = a * 2 -- 10 ns
+
+ -- 循环 n 次
+ for _ = 1, n do -- 1 ns
+ print(0) -- 5 ns
+ end
+ end
+ ```
+
根据以上方法,可以得到算法的运行时间为 $(6n + 12)$ ns :
$$
@@ -484,6 +500,29 @@ $$
end
```
+=== "Lua"
+
+ ```lua title=""
+ ---算法 A 的时间复杂度:常数阶
+ local function algorithm_A(n)
+ print(0)
+ end
+
+ ---算法 B 的时间复杂度:线性阶
+ local function algorithm_B(n)
+ for _ = 1, n do
+ print(0)
+ end
+ end
+
+ ---算法 C 的时间复杂度:常数阶
+ local function algorithm_C(n)
+ for _ = 1, 1000000 do
+ print(0)
+ end
+ end
+ ```
+
下图展示了以上三个算法函数的时间复杂度。
- 算法 `A` 只有 $1$ 个打印操作,算法运行时间不随着 $n$ 增大而增长。我们称此算法的时间复杂度为“常数阶”。
@@ -683,6 +722,21 @@ $$
end
```
+=== "Lua"
+
+ ```lua title=""
+ local function algorithm(n)
+ local a = 1 -- +1
+ a = a + 1 -- +1
+ a = a * 2 -- +1
+
+ -- 循环 n 次
+ for i = 1, n do -- +1
+ print(0) -- +1
+ end
+ end
+ ```
+
设算法的操作数量是一个关于输入数据大小 $n$ 的函数,记为 $T(n)$ ,则以上函数的操作数量为:
$$
@@ -960,6 +1014,27 @@ $T(n)$ 是一次函数,说明其运行时间的增长趋势是线性的,因
end
```
+=== "Lua"
+
+ ```lua title=""
+ local function algorithm(n)
+ local a = 1 -- +0(技巧 1)
+ a = a + n -- +0(技巧 1)
+
+ -- +n(技巧 2)
+ for i = 1, 5 * n + 1 do
+ print(0)
+ end
+
+ -- +n*n(技巧 3)
+ for i = 1, 2 * n do
+ for j = 1, n + 1 do
+ print(0)
+ end
+ end
+ end
+ ```
+
以下公式展示了使用上述技巧前后的统计结果,两者推算出的时间复杂度都为 $O(n^2)$ 。
$$
diff --git a/docs/chapter_data_structure/basic_data_types.md b/docs/chapter_data_structure/basic_data_types.md
index 33ec9f77dd..829576beeb 100644
--- a/docs/chapter_data_structure/basic_data_types.md
+++ b/docs/chapter_data_structure/basic_data_types.md
@@ -170,6 +170,19 @@
data = [0, 0.0, 'a', false, ListNode(0)]
```
+=== "Lua"
+
+ ```lua title=""
+ -- Lua 中没有严格的类型系统,数据类型通过上下文推断
+ -- Lua 中也没有专门的数组类型,而是使用 table 实现数组/列表的功能。
+ local numbers = {0, 0, 0, 0, 0}
+ local decimals = {0.0, 0.0, 0.0, 0.0, 0.0}
+ local characters = {'0', '0', '0', '0', '0'}
+ local bools = {false, false, false, false, false}
+ -- Lua 的 table 可以自由存储各种基本数据类型和对象引用
+ local data = [0, 0.0, 'a', false, ListNode(0)]
+ ```
+
??? pythontutor "可视化运行"
https://pythontutor.com/render.html#code=class%20ListNode%3A%0A%20%20%20%20%22%22%22%E9%93%BE%E8%A1%A8%E8%8A%82%E7%82%B9%E7%B1%BB%22%22%22%0A%20%20%20%20def%20__init__%28self,%20val%3A%20int%29%3A%0A%20%20%20%20%20%20%20%20self.val%3A%20int%20%3D%20val%20%20%23%20%E8%8A%82%E7%82%B9%E5%80%BC%0A%20%20%20%20%20%20%20%20self.next%3A%20ListNode%20%7C%20None%20%3D%20None%20%20%23%20%E5%90%8E%E7%BB%A7%E8%8A%82%E7%82%B9%E5%BC%95%E7%94%A8%0A%0A%22%22%22Driver%20Code%22%22%22%0Aif%20__name__%20%3D%3D%20%22__main__%22%3A%0A%20%20%20%20%23%20%E4%BD%BF%E7%94%A8%E5%A4%9A%E7%A7%8D%E5%9F%BA%E6%9C%AC%E6%95%B0%E6%8D%AE%E7%B1%BB%E5%9E%8B%E6%9D%A5%E5%88%9D%E5%A7%8B%E5%8C%96%E6%95%B0%E7%BB%84%0A%20%20%20%20numbers%20%3D%20%5B0%5D%20*%205%0A%20%20%20%20decimals%20%3D%20%5B0.0%5D%20*%205%0A%20%20%20%20%23%20Python%20%E7%9A%84%E5%AD%97%E7%AC%A6%E5%AE%9E%E9%99%85%E4%B8%8A%E6%98%AF%E9%95%BF%E5%BA%A6%E4%B8%BA%201%20%E7%9A%84%E5%AD%97%E7%AC%A6%E4%B8%B2%0A%20%20%20%20characters%20%3D%20%5B'0'%5D%20*%205%0A%20%20%20%20bools%20%3D%20%5BFalse%5D%20*%205%0A%20%20%20%20%23%20Python%20%E7%9A%84%E5%88%97%E8%A1%A8%E5%8F%AF%E4%BB%A5%E8%87%AA%E7%94%B1%E5%AD%98%E5%82%A8%E5%90%84%E7%A7%8D%E5%9F%BA%E6%9C%AC%E6%95%B0%E6%8D%AE%E7%B1%BB%E5%9E%8B%E5%92%8C%E5%AF%B9%E8%B1%A1%E5%BC%95%E7%94%A8%0A%20%20%20%20data%20%3D%20%5B0,%200.0,%20'a',%20False,%20ListNode%280%29%5D&cumulative=false&curInstr=12&heapPrimitives=nevernest&mode=display&origin=opt-frontend.js&py=311&rawInputLstJSON=%5B%5D&textReferences=false
diff --git a/docs/chapter_hashing/hash_algorithm.md b/docs/chapter_hashing/hash_algorithm.md
index 0ecf1f1411..fca6e0e2e6 100644
--- a/docs/chapter_hashing/hash_algorithm.md
+++ b/docs/chapter_hashing/hash_algorithm.md
@@ -399,6 +399,12 @@ $$
# 节点对象 #