可能想到递归,就想到了生兔子、走台阶(这两个应该都是斐波那契吧)、阶乘、二叉树的前中后(深度遍历)、无限分类、打印目录文件。其实我就能想到这么多了。额,等等是不是把汉诺塔忘了。
平常工作中你用到哪些递归了呢?
我说说我的吧。商品分类、商品SKU,然后就没有了。
商品分类无非总是根据子找父类。分类树形展示如何如何。
/**
* @author yan@yan.com
* @desc 根据子id获取父ID。通过level指定获取到哪一层的父ID
* @param int $catId
* @param int $level 默认追到第一级
*/
public static function getCatIdBySubCatId(int $catId, int $catLevel=1) {
$where = [
'where'=>[
'cate_id'=>$catId
]
];
$result = self::find($where);
if ($result['cate_level'] < $catLevel || empty($result)) { // 说明不符合条件
return 0;
}
if ($result['cate_level'] == $catLevel) {// 如果递归的是目标级别也不要再递了
return $result['cate_id'];
}
return self::getCatIdBySubCatId($result['cate_pid'], $catLevel);
}
还有一个需求是,商品自定义编号。前两位是一级分类的编号,再两位是二级分类的编号。之后四位是该分类(也有可能是三级分类,但是编号止步于二级。但如果只是1级目录下加商品那么一级目录的编号跟00即可,因为没有用到二级。实际上基本都是三级。)下商品的自增的编号。
/**
* @author yan@yan.com
* @desc 返回一级二级的分类编号
* @param int $catId
* @param int $level
* @return string
*/
public static function getSerialNumber(int $catId,int $level=0) {
static $numStr = '';
if ($level == 0) {
$numStr = ''; // 第一次调用就将该函数的静态变量给初始化
}
$where = [
'where'=>[
'cate_id'=>$catId
]
];
$result = self::find($where);
if (empty($result)) {
return $numStr;
}
if ($result['cate_level'] == 1 && $level==0) {
return $result['serial_number'].'00'; // 如果只是一级类目那么就补00
} else {
$numStr = $result['serial_number'].$numStr;
}
$result = self::find($where);
self::getSerialNumber($result['cate_pid'], $level+1);
return $numStr;
}
那个商品的sku呢。是前端代码(之前有一篇说了下对前端的一次重构)。后来写完觉得就是一个笛卡尔积。效果如下

其实后来写完之后,也对写的不满意。代码一多就不方便别人看懂了。当然有些优化点,比如解析赋值的特性也会省几行。
assembleSkuList1(data, skuAttr, arr, start) { // 递归组合sku: data 是从服务器获取的 属性list,skuArr是递归后组合的商品的skuList ,arr是不同规格一组,start 其实
if (data.length === start) { // 递归出口,几个规格就 start
return skuAttr
}
if (data[start].attr_value.length === 0) { // 因为规格 可以删除,这里为了健壮性要加默认值
const tempObj = {}
tempObj.attr_id = data[start].attr_id
tempObj.attr_name = data[start].attr_name
arr[start] = tempObj
}
// 下面是递归的主要逻辑
for (let i = 0; i < data[start].attr_value.length; i++) {
arr = deepClone(arr) // 深度拷贝,避免出错
const tempObj = data[start].attr_value[i]
tempObj.attr_id = data[start].attr_id
tempObj.attr_name = data[start].attr_name
arr[start] = tempObj // 几个规格就几个一组,用arr保存一小组
if (arr.length === data.length) {
skuAttr.push({
sku_attr: arr,
sku_org_price: this.batchElements.sku_org_price,
sku_price: this.batchElements.sku_price,
vip_price: this.batchElements.vip_price,
sku_stock: this.batchElements.sku_stock,
weight: this.form.product_weight,
sku_id: 0
})
}
if (data[start + 1] !== undefined) { // 如果有下一个规格就递归下去,因为 属性是多维
this.assembleSkuList(data, skuAttr, arr, start + 1)
}
}
return skuAttr
}
写与调
考虑好出口边界。
写那个sku的时候。我先在纸上写了三排规格。然后想怎么组合。然后想到用递归正合适。用笔划了划线。就开始写了。 后来陷入了bug中。又一直用console打印去看打印结果。当然你可以debugger,用鼠标看,但是打印出来的比较直观。
用几个数据先验证。如果正确,尝试用更多的数据。当然像分类跟sku。没多少数据。但可先用四五条,后再用多条。
两个算法动画网站 >https://www.cs.usfca.edu/~galles/visualization/Algorithms.html
https://visualgo.net/en
参考资料:
- 《数据结构与算法之美》王争(极客时间专栏)
网友评论