所谓递归算法,是将问题转化为规模缩小的同类问题的子问题,每一个子问题都用一个同样的算法去解决。一般来说,一个递归算法就是函数调用自身去解决它的子问题。
使用递归算法实现:大题的分数值等于其下各小题的分数值之和。
本文讲解所用到的数据集合(已是树形结构)。
一、节点数据集合
var nodes = [{"id": "EQ06-9006C8BBE68A475BAC04858F60E0249D","name": "题目1","type": "解答题","children": [{"id": "EQ06-CC80CFD8702F47E4A42AF63B0BA10569","name": "题目1.1","type": "解答题","score": 5}, {"id": "EQ06-06F7EBA807D445EB937B920ED2250016","name": "题目1.2","type": "解答题","score": 6}],"score": 10}, {"id": "EQ06-0F0A8DA4B5834D1BB96504546EA47DA9","name": "题目2","type": "解答题","children": [{"id": "EQ06-660D9E79B5054EA296D15EFA6D1F5462","name": "题目2.1","type": "解答题","score": 10}, {"id": "EQ06-C5D5191018B94E8C8D1E9AB3AD5E4C61","name": "题目2.2","type": "解答题","score": 20,"children": [{"id": "EQ06-037CBCDFD897434A839619D4AF07E030","name": "题目2.2.1","type": "解答题","score": 5}, {"id": "EQ06-FC7CAE767F4E48D6B3E0B78D5E4BC473","name": "题目2.2.2","type": "解答题","score": 6}, {"id": "EQ06-FC7CAE767F4E48D6B3E0B78D5E4BC474","name": "题目2.2.3","type": "解答题","score": 7}]}],"score": 40}];起初朋友给我发了一段数据,输出的是题目节点信息,其中包含题目的分数值:score,原始数据已经过树形结构处理了。
他的需求是:题目的分数值 score 是创建题目时预估定义的一个分数值,如20分;实际情况是在某个大题的下面还可以创建小题,小题也会定义一个分数值 score,这样一来,某个大题原先预估定义的分数值 score 可能与它下面所有小题的分数值 score 加起来的和并不相等,这样大题的分数值 score 就显得不准确,希望我在前端能帮忙处理一下,使得大题的分数值等于汇总了其下所有小题的分数值之和,以符合题目分数在实际展现时的准确无误。
接到朋友的这个 issue,我想了一下,大致要求就是针对于某个节点的属性(这里是 score),汇总累加其下所有子节点的这个属性(score)的数值,然后将这一过程中累加得到的数值赋到父节点的这个属性(score)上。
为了实现树型结构数据的遍历与处理,免不了要想到使用递归函数来实现。我们先来看一下定义,所谓递归算法,是将问题转化为规模缩小的同类问题的子问题,每一个子问题都用一个同样的算法去解决。一般来说,一个递归算法就是函数调用自身去解决它的子问题。
递归算法的特点:
在函数过程中调用自身。
在递归过程中,必须有一个明确的条件判断递归的结束,既递归出口。
递归算法简洁但效率低,通常不作为推荐算法。
上面这是原理性的解释,讲的也是十分明确,下面我们结合实例来细细琢磨。
二、编写一个递归算法,实现汇总累加节点的分数值
var getScoreSum = function (data, options){//默认参数配置var options = {scoreField: options.scoreField || "score", //分数属性名childrenField: options.childrenField || "children", //子节点集合属性名spread: options.spread || false};var scoreSum = 0; //定义父级节点汇总分数的局部变量if(data && data.length > 0){for(var i = 0, item; i < data.length; i++){item = data[i];var itemScore = item[options.scoreField] || 0, itemChildren = item[options.childrenField] || []; //定义本次循环中的局部变量var tempSum = 0; //定义当前条目汇总分数的临时变量//是否有子节点if(itemChildren && itemChildren.length > 0){var tempCldSum = getScoreSum(itemChildren, options) || 0; //递归调用 getScoreSum() 方法,获取当前节点下的子节点集合的汇总分数 tempCldSumtempSum += tempCldSum; //子节点集合的汇总分数 tempCldSum,追加到当前节点汇总分数 tempSum 上}else{tempSum += itemScore; //没有子节点,以当前节点的分数值追加到当前节点汇总分数 tempSum 上delete item[options.childrenField]; //删除属性}item["scoreSum"] = tempSum; //已有或一个新的节点属性:scoreSum,并使用当前节点汇总分数 tempSum 赋值item["name"] = item.name + '(' + (item.scoreSum || item.score) + ' 分)' //其他属性设置item["spread"] = options.spread;scoreSum += tempSum; //当前节点汇总分数追加到父级节点汇总分数上}}return scoreSum;}在方法中用到了调用方法自身的执行语句,目的是遍历所有子节点,做相同的累加处理。
三、调用递归函数,并测试题目的分数值在实际数据展现时是否准确无误
//layui 模块化组件引用layui.use(['jquery', 'layer', 'tree'], function () {var $ = layui.$, layer = layui.layer, tree = layui.tree;//分数汇总处理getScoreSum(nodes, {scoreField: "score", childrenField: "children", spread: true});//console.log(nodes);//构建树节点var buildTreeNodes = function (options) {options = options || {};var treeNodes = [{ name: "全部分组", spread: true, children: nodes }];$("#treeGroup").empty(); //先清空DOMlayui.tree({elem: '#treeGroup',nodes: treeNodes,click: function (node) {var self = $(this.elem);var nodeLinks = self.find("li>a");nodeLinks.removeClass("active");nodeLinks.filter(function (index) {return $('cite', this).text() == node.name;}).addClass('active');nodeLinks.splice(0, 1); //删除数组第一项:全部分组console.log(node); //node即为当前点击的节点数据document.getElementById("nodeContent").innerhtml = JSON.stringify(node, null, 2); //使用JSON.stringify() 格式化输出JSON字符串}});if (options.selectedNodeName) {var elem = $("#treeGroup");var nodeLinks = elem.find("ul>li>a");nodeLinks.removeClass("active");nodeLinks.filter(function (index) {return $('cite', this).text() == options.selectedNodeName;}).trigger('click');}}buildTreeNodes();});在这里我用的是 layui 框架中的 tree 组件来展现递归处理后的数据。新建一个 scoreSum.html 和 scoreSum.js 文件,在 scoreSum.html 中放一个 id="tree" 的元素,用来加载递归处理汇总累加分数值之后的树形节点数据。
scoreSum.html 页面的代码如下:
<!DOCTYPE html><html><head><meta charset="utf-8"><meta name="renderer" content="webkit"><meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1"><meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1"><meta name="apple-mobile-web-app-status-bar-style" content="black"><meta name="apple-mobile-web-app-capable" content="yes"><meta name="format-detection" content="telephone=no"><title>使用递归汇总累加题目分数值</title><link rel="stylesheet" href="assets/layui/css/layui.css" media="all" /><style type="text/css">body{margin: 12px;}.layui-tree li a.active{border-color: lightblue;background-color: #b1deff;}</style></head><body><div class="layui-row layui-col-space20" style="margin-top: 15px;"><div class="layui-col-md4"><fieldset class="layui-elem-field"><legend>题目索引</legend><blockquote class="layui-elem-quote layui-quote-nm layui-bg-gray"><p>大题下面有小题,大题的分数是汇总其下各小题的分数</p></blockquote><div class="layui-field-box"><ul id="tree"><li class="text-muted">正在加载...</li></ul></div></fieldset></div><div class="layui-col-md8"><fieldset class="layui-elem-field"><legend>题目信息</legend><div class="layui-field-box"><blockquote class="layui-elem-quote"><p><i class="layui-icon"></i> 题目的分数值以“scoreSum”为准,“score”是从数据库中读出来的,仅作参考。</p></blockquote><blockquote class="layui-elem-quote layui-quote-nm"><pre id="nodeContent"><span class="layui-word-aux">点击左边节点,以加载节点信息……</span></pre></blockquote></div></fieldset></div></div><script type="text/javascript" src="assets/layui/layui.js"></script><script type="text/javascript" src="scoreSum.js"></script></body></html>scoreSum.js 存放的就是上面几段脚本:
//节点数据集合var nodes = [{……}];//使用递归算法,实现汇总累加节点的分数值var getScoreSum = function (data, options){……}//layui 模块化组件引用layui.use(['jquery', 'layer', 'tree'], function () {var $ = layui.$, layer = layui.layer, tree = layui.tree;……buildTreeNodes();});四、效果截图
效果截图:大题的分数值是其下小题分数值的累计之和(11=5+6)。
本案例完整的项目文件目录截图。
还没有评论,来说两句吧...