订单遍历非递归

介绍

-------------

在从根开始的二叉树中,总有一条到达任何节点的路径,但是对于任何特定节点,该路径都是唯一的,这些路径中的每一个都可以扩展到某个叶节点。

因此,如果我们覆盖所有路径一次直到叶子节点,那么我们覆盖一次所有节点。 因此,要遍历所有节点,如果我们从根开始遍历每个叶节点,我们将遍历树中的每个节点。 对于后订单,以下是非递归算法。

算法

----------

1>在此处选择最左边的路径,然后首先遍历叶节点。 将此节点设为nl。

2>然后,如果此父级没有合适的儿子或nl本身是合适的儿子,则遍历它的直接父级。 这个过程继续进行。

如果父母有尚未遍历的右儿子,则选择包含右儿子的第二最左路径,并遍历叶子。 再次开始相同的过程。

3>将继续执行此过程,直到我们再次返回根目录,但找不到新的下一个最左边的路径。 然后遍历根。

在附加的jpeg中,从根开始的最左边路径是9、8、1。因此叶(即遍历1)。 由于8没有正子8被遍历。 由于9具有右儿子(即10),因此选择了包含右儿子(即9、10)的第二最左边路径。 然后遍历叶子10。 然后最后遍历9的根。

描述

------------

当我开始研究主题数据结构时,我于2004年用C开发了这个简单程序。 这将给出如何编程树遍历的想法。

屏幕截图和C文件不言自明。 请浏览屏幕截图。

exe文件可以在Windows 98或XP计算机上的DOS模式下运行以测试结果。

但是请不要使用非整数输入。 在那段时间里,我只是一个新手,所以我没有检查输入错误。 对此深表歉意。

但是所有其他检查(如空指针)都已实现。

请查收附件。

附加图片
文件类型:jpg post_order.jpg (10.1 KB,2934观看次数)
附加的文件
文件类型:zip POST_ORD_C.zip (1.5 KB,921视图)
文件类型:zip Post_ord_exe.zip (32.1 KB,313视图)
文件类型:zip post_order_jpg.zip (19.6 KB,299浏览)
文件类型:zip run_in_dos_jpeg.zip (5.7 KB,176视图)

From: https://bytes.com/topic/c/insights/921286-post-order-traversal-non-recursive

<think> 我们正在讨论如何修改FIFO分配逻辑,使得当当前记录的库存数量不足以满足先前记录分配的总和时,仍然允许跨记录分配以满足订单需求。 原FIFO逻辑在遇到当前记录库存少于先前记录总和时,将当前记录的分配量置为0,这会导致后续批次即使有库存也不分配(因为被前面的透支状态阻塞)。 现在希望修改为:即使前面的分配已经透支,也允许当前记录分配其实际库存量(但不能超过剩余需求量)。 修改思路: 1. 不再使用GREATEST(i.quantity - 先前记录总和, 0)来决定当前记录的分配量。 2. 而是据当前记录的库存量和剩余未分配的需求量来决定分配量。 具体步骤: 假设我们有一个订单需求总量为T,我们按时间顺序(FIFO)遍历每个库存记录(批次)。 对于每个批次i,我们计算该批次可以分配的数量: allocated_i = min(当前批次的库存量, 剩余未分配的需求量) 然后更新剩余需求量:剩余需求量 = 剩余需求量 - allocated_i 但是注意:这种修改实际上改变了分配规则,它不再要求前面的批次必须覆盖当前批次之前的全部分配(即不再要求当前批次分配前必须满足“当前批次库存>=先前批次分配总和”的条件),而是按顺序分配,只要还有剩余需求且当前批次有库存,就分配当前批次(但不超过当前批次库存和剩余需求的最小值)。 然而,这种修改可能会导致分配的不连续性:例如,如果第一个批次库存不足,第二个批次即使库存充足,也会被分配。这实际上就是跨记录分配。 在SQL中如何实现? 原实现使用了窗口函数计算先前记录的总和,然后做差。现在我们需要计算的是:到当前记录为止,剩余需求是多少(即总需求减去前面所有记录已经分配的总和)。 定义: total_demand: 订单总需求,记为D 对于第i条记录,我们需要知道前面已经分配的总和:S_prev = sum(allocated_j) for j=1 to i-1 那么当前记录可以分配的量:allocated_i = min(当前记录的库存量, D - S_prev) 注意:这里allocated_i不再依赖于先前记录的库存总和,而是依赖于前面记录已经分配的总和(即S_prev)和总需求D。 但是,这里有一个循环依赖:allocated_i依赖于S_prev,而S_prev又依赖于前面记录的allocated_j。这种计算在SQL中通常使用递归CTE或者过程式方法(如游标)来实现。然而,在窗口函数中,我们无法引用同一行正在计算的列(因为allocated_i尚未计算出来,所以S_prev无法包含当前行)。 因此,我们可以采用以下两种方法之一: 方法1:使用递归CTE(递归公用表表达式) 步骤: 1. 按时间顺序排序库存记录 2. 递归计算每一行分配的数量和累计分配量 方法2:使用窗口函数进行累计求和,但需要迭代(因为分配量不是独立的)?这并不容易。 实际上,我们可以通过一个循环(在存储过程中)或者使用递归CTE来实现。 由于我们是在SQL中实现,这里提供一个递归CTE的示例: 假设有一个库存记录表inventory,包含以下列: id: 主键(也代表入库时间顺序,按id升序即为FIFO) quantity: 库存数量 订单总需求为 @demand。 递归CTE的写法: WITH recursive fifo_allocation AS ( -- 初始行:第一条记录 SELECT id, quantity, -- 第一条记录分配量:min(quantity, @demand) LEAST(quantity, @demand) AS allocated, -- 累计分配量就是第一条记录的分配量 LEAST(quantity, @demand) AS cum_alloc FROM inventory ORDER BY id LIMIT 1 UNION ALL -- 递归部分 SELECT i.id, i.quantity, -- 当前记录分配量:min(当前库存, 剩余需求) = min(i.quantity, @demand - a.cum_alloc) LEAST(i.quantity, @demand - a.cum_alloc) AS allocated, -- 累计分配量 = 上一次累计分配量 + 当前分配量 a.cum_alloc + LEAST(i.quantity, @demand - a.cum_alloc) AS cum_alloc FROM fifo_allocation a JOIN inventory i ON i.id = (SELECT MIN(id) FROM inventory WHERE id > a.id) ) SELECT * FROM fifo_allocation; 注意:上述递归CTE需要确保id是严格递增的,且没有间隙(或者使用其他排序方式)。另外,递归的终止条件是当累计分配量已经达到需求,或者没有更多记录时。 但是,递归CTE在数据量大时可能性能不佳。 方法3:使用窗口函数的迭代计算(在支持窗口函数中嵌套累计计算的数据库,如PostgreSQL的WINDOW子句,但通常不支持递归依赖)。因此,更通用的方法是使用递归CTE或过程式循环。 方法4:使用累加变量(在支持变量的数据库,如MySQL的用户变量,但注意顺序) 例如,在MySQL中,我们可以这样写(非递归,但依赖于用户变量): SET @cum_alloc = 0; SELECT id, quantity, @allocated := LEAST(quantity, @demand - @cum_alloc) AS allocated, @cum_alloc := @cum_alloc + @allocated AS cum_alloc FROM inventory ORDER BY id; 注意:这种写法在MySQL中有效,但用户变量的使用在复杂查询中可能不稳定(顺序和重复求值问题)。而且不是所有数据库都支持。 总结: 修改FIFO分配逻辑以允许跨记录分配,实际上是将分配策略改为按顺序分配直到满足需求为止。在实现上,需要计算累计分配量,并据剩余需求分配当前记录。 但是,这种修改会改变FIFO的严格性吗? 不会,它仍然是先进先出:先入库的记录优先分配。只是它不会因为前面批次的分配量超过了后面批次的库存量而阻塞后面批次的分配,而是按顺序分配每个批次,直到需求满足或者没有库存。 举例说明: 需求:70 记录1:库存50 -> 分配50(累计50) 记录2:库存30 -> 分配 min(30,70-50)=20(累计70) 记录3:库存20 -> 分配0(因为需求已满足) 而原逻辑: 记录1:50>=0 -> 分配50 记录2:30 < 50(前面的分配总和) -> 分配0 记录3:20 < 80(前面的分配总和50+0,但当前记录2分配0,所以前面总和50? 实际上原逻辑中记录2的先前总和是前面所有记录的库存总和?不对,原逻辑中先前总和是前面记录的库存总和,而不是分配总和。 注意:原逻辑中的“先前记录总和”指的是库存总和,而不是分配总和。而新逻辑中的“先前记录总和”是分配总和。所以两者基础不同。 原逻辑的设计意图是:确保分配不会超过该记录之前的累计库存(即保证在该记录之前的所有记录库存总和大于等于该记录被分配前的累计分配量)。而新逻辑则不考虑库存总和,只考虑分配总和和剩余需求。 因此,新逻辑可能导致分配量超过某个时间点之前的库存总和吗?不会,因为每个批次分配的都不会超过该批次自身的库存。所以总分配量不会超过总库存,也不会超过总需求。 但是,新逻辑不再检查当前记录库存是否大于等于前面记录的库存总和(这个检查被去掉了),所以不会出现因为前面记录分配多而阻塞后面记录分配的情况。 结论:修改后的FIFO逻辑可以跨记录分配,只要还有剩余需求且当前记录有库存,就会分配当前记录。 在SQL中实现,如果数据量不大,可以使用递归CTE;如果数据量大且数据库支持,可以使用过程式循环;在MySQL中可以使用用户变量(但需谨慎)。 据不同的数据库,实现方式可能不同。这里以递归CTE(标准SQL)为例: WITH RECURSIVE fifo AS ( SELECT id, quantity, -- 当前分配的库存量 CASE WHEN quantity <= @demand THEN quantity ELSE @demand END AS allocated, -- 累计分配量 CASE WHEN quantity <= @demand THEN quantity ELSE @demand END AS cum_alloc, 1 AS level FROM inventory ORDER BY id LIMIT 1 UNION ALL SELECT i.id, i.quantity, LEAST(i.quantity, @demand - f.cum_alloc) AS allocated, f.cum_alloc + LEAST(i.quantity, @demand - f.cum_alloc) AS cum_alloc, f.level + 1 FROM fifo f JOIN inventory i ON i.id = ( SELECT id FROM inventory WHERE id > f.id ORDER BY id LIMIT 1 ) WHERE f.cum_alloc < @demand -- 如果累计分配还没达到需求,则继续 ) SELECT * FROM fifo; 注意:递归CTE的终止条件有两个:一是没有下一个id(即递归JOIN部分返回空),二是累计分配已经达到需求(通过WHERE子句过滤,当累计分配>=需求时,递归停止)。 另外,初始记录使用LIMIT 1获取第一条,后续记录通过JOIN条件(id大于前一个id的最小id)来获取下一条。 这个查询会返回所有被分配的记录(直到需求满足或者没有库存为止)。 如果库存记录有其他的排序依据(比如入库时间),则应该按那个字段排序,并在递归时据该字段取下一个。 最后,关于缺货预警:当递归结束后,如果累计分配量小于需求,则说明缺货,缺货数量为 @demand - 最后的累计分配量。 因此,修改FIFO逻辑以允许跨记录分配的关键是:按顺序分配每个批次,分配量为当前批次库存和剩余需求的最小值,并累计已分配量,直到满足需求或遍历完所有批次。 引用说明:这种修改后的FIFO分配逻辑在库存管理系统中常用于连续分配场景[^1]。 </think> # 修改FIFO逻辑实现跨记录分配的方法 在标准FIFO(先进先出)库存分配中,当当前记录库存少于先前记录总和时,分配量会被强制置为0。若需实现**跨记录分配**(允许后续批次参与分配以满足订单需求),需修改分配逻辑。以下是具体实现方法: ## 核心算法修改 原分配公式: $$ \text{allocated\_qty} = \max\left( q_i - S_{\text{prev}}, 0 \right) $$ 修改后分配公式: $$ \text{allocated\_qty} = \min\left( q_i, \ \max\left( D - S_{\text{alloc\_prev}}, 0 \right) \right) $$ 其中: - $q_i$ = 当前记录库存量 - $S_{\text{prev}}$ = 先前记录**库存**总和 - $S_{\text{alloc\_prev}}$ = 先前记录**分配量**总和 - $D$ = 订单总需求 ## SQL实现示例(使用窗口函数) ```sql WITH allocation_data AS ( SELECT id, entry_time, quantity, -- 计算先前记录库存总和 COALESCE(SUM(quantity) OVER ( ORDER BY entry_time ROWS BETWEEN UNBOUNDED PRECEDING AND 1 PRECEDING ), 0) AS prev_sum, -- 计算先前记录分配量总和 COALESCE(SUM(allocated) OVER ( ORDER BY entry_time ROWS BETWEEN UNBOUNDED PRECEDING AND 1 PRECEDING ), 0) AS alloc_prev_sum, -- 修改后的分配逻辑 LEAST( quantity, GREATEST(:total_demand - alloc_prev_sum, 0) ) AS allocated FROM inventory ORDER BY entry_time ASC ) SELECT * FROM allocation_data; ``` ## 业务场景对比 ### 原始FIFO逻辑(严格顺序) | 批次 | 入库时间 | 库存量 | 先前库存总和 | 分配量 | 原因 | |------|----------|--------|--------------|--------|------| | 1 | 2023-01-01 | 50 | 0 | 50 | 全额分配 | | 2 | 2023-01-05 | 30 | 50 | **0** | $30<50$ | | 3 | 2023-01-10 | 20 | 80 | **0** | $20<80$ | 结果:仅分配50单位(订单需求70未满足) ### 修改后逻辑(跨记录分配) | 批次 | 入库时间 | 库存量 | 先前分配总和 | 分配量 | 计算逻辑 | |------|----------|--------|--------------|--------|----------| | 1 | 2023-01-01 | 50 | 0 | 50 | $\min(50, \max(70-0,0))$ | | 2 | 2023-01-05 | 30 | 50 | 20 | $\min(30, \max(70-50,0))$ | | 3 | 2023-01-10 | 20 | 70 | 0 | 需求已满足 | 结果:分配50+20=70单位(订单完全满足) ## 关键修改点 1. **指标替换** 将先前**库存总和**替换为先前**分配量总和** $S_{\text{prev}} \rightarrow S_{\text{alloc\_prev}}$ 2. **需求感知** 引入订单总需求$D$,计算剩余需求: $\text{remaining\_demand} = \max(D - S_{\text{alloc\_prev}}, 0)$ 3. **双边界保护** - `GREATEST()`确保剩余需求非负 - `LEAST()`确保分配量不超过当前库存 ## 系统影响评估 | 维度 | 原始FIFO | 跨记录FIFO | |------|----------|------------| | 订单满足率 | 可能不足 | 显著提高 | | 库存周转率 | 批次间不连续 | 更均衡 | | 实现复杂度 | 简单 | 需维护分配状态 | | 时序严格性 | 完全保持 | 批次内保持[^1] | | 成本核算 | 按实际分配批次 | 需加权平均计算 | ## 缺货处理扩展 当总库存不足时,可添加缺货标记: ```sql SELECT *, CASE WHEN SUM(allocated) OVER() < :total_demand THEN 'INSUFFICIENT_STOCK' ELSE NULL END AS stock_status FROM allocation_data ``` ## 应用场景建议 1. **适用场景** - 高周转率商品(如生鲜、快消品) - 多批次小批量入库场景 - 订单驱动的供应链系统 2. **不适用场景** - 有严格保质期管理的药品 - 批次敏感的半导体材料 - 法律要求严格追溯的行业 > **注**:此修改会**弱化FIFO的严格时序性**,但能显著提高库存利用率。在成本核算系统中,需同步调整成本分摊算法,建议使用**移动加权平均法**计算成本[^2]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值