【语法】
一、机制原理
隐蔽的坑:优先任务调度器上线,每次取最高优先级任务都全表排序一次,任务池过千后调度本身成了大头。二叉堆是正解:数组摆成满层二叉树,下标 i 的孩子是 2i 与 2i+1,父节点恒不大于孩子(小顶堆)。两个核心动作各走一条路径:插入时新元素放末位一路上浮;弹出时末位补顶一路下沉。建堆更是妙手:从最末的非叶节点倒序逐个下沉,乱序数组一趟就理成堆,整体 O(n)。
二、错误写法
-- 错误:每次取最值全表排序,任务池越大越亏
table.sort(pool, function(a, b)
return a.pri < b.pri
end)
return table.remove(pool, 1)
三、正确写法
local heap = {}
local function sink(i)
local n = #heap
while i * 2 <= n do
local m = i * 2
if m + 1 <= n and heap[m + 1] < heap[m] then
m = m + 1
end
if heap[m] >= heap[i] then break end
heap[i], heap[m] = heap[m], heap[i]
i = m
end
end
local function push(v)
heap[#heap + 1] = v
local i = #heap
while i > 1 and heap[math.floor(i / 2)] > heap[i] do
local p = math.floor(i / 2)
heap[p], heap[i] = heap[i], heap[p]
i = p
end
end
local function pop()
local top = heap[1]
local last = table.remove(heap)
if #heap > 0 then
heap[1] = last
sink(1)
end
return top
end
local function build(arr)
heap = arr
for i = math.floor(#heap / 2), 1, -1 do
sink(i)
end
end
build({9, 4, 7, 1, 8, 2})
local label = panel:getChildByName("heapText")
label:setString(tostring(pop() == 1) .. "/" .. tostring(pop() == 2))
四、引擎验证
乱序数组建堆后连续弹出,出列次序严格按值升序;建堆一轮 O(n),后续插弹各走单路径。
五、FAQ
问:建堆为什么从非叶节点倒着来?
答:叶节点天然成堆,倒序下沉让每棵子树自底向上先理好。
问:插入与弹出的开销?
答:都只走一条叶到根或根到叶的路径,对数级。
全站技术干货持续更新:996 引擎 / Lua 实战帖,语法、参数与示例一篇讲透。进入文章地图 · 查看全部 →
【语法算法】 元方法 __len 的触发就这一行: 对带 __len 的表做 t 操作时,Lua 调用 __len 而不是返…
【语法算法】 load 的编译本质就这一行: load 把字符串编译成函数——字符串里的代码被编译成可调用的函数——调用返回…
【语法算法】 error 的传播本质就这一行: error 抛出一个错误——第二个参数指定错误信息的层级——层级 2 表示把…
【语法算法】 pcall 的保护调用本质就这两行: pcall 把函数包在保护壳里执行——函数内部报错不会传播到外层——ok…
【语法算法】 元方法 __tostring 的触发就这一行: 对带 __tostring 的表做 tostring(t) 或…
【语法算法】 元方法 __call 的触发就这一行: 对带 __call 元方法的表做函数调用 t(...) 时,Lua 不…