js 迷宫最短路,使用uikit和jQuery dijkstra
2017-09-24 00:00
295 查看
uikit美化 + 栈寻找所有路径
dijkstra优化,但是由于路径数目增加太快,所以还是有限制
增加了一个判断路径数目是否过多的函数,如果路径数目过多则不进行查找
![](https://static.oschina.net/uploads/space/2017/0926/130745_vPao_2856757.png)
比较丑....
dijkstra优化,但是由于路径数目增加太快,所以还是有限制
增加了一个判断路径数目是否过多的函数,如果路径数目过多则不进行查找
![](https://static.oschina.net/uploads/space/2017/0926/130745_vPao_2856757.png)
<!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8"> <link rel="stylesheet" href="css/uikit.min.css"/> <script src="js/jquery.js"></script> <script src="js/uikit.min.js"></script> <script src="js/uikit-icons.min.js"></script> <meta name="viewport" content="width=device-width, initial-scale=1"> <meta charset="UTF-8"> <title>迷宫寻路--阿豪</title> <script type="text/javascript"> //全局颜色 colors = ['black', //0障碍物颜色,不能走 'lightgray', //1,普通路颜色 'yellow', //2,端点颜色 'deepskyblue', //3,路径颜色 ] rows = 10 //行数 cols = 10 //列数 cell_width = 20 //格子宽度 cell_height = 20//格子高度 g = getMat(rows, cols, 1) cur_num = 0 all_num = 0 paths = new Array() ps = new Array() //保存两个端点 dir = [[-1, 0], [0, 1], [1, 0], [0, -1]] //方向,上右下左 //添加字符串格式化函数,方便使用 String.prototype.format = function () { var args = arguments; return this.replace(/\{(\d+)\}/g, function (m, i) { return args[i]; }); } //创建矩阵的辅助函数 function getMat(x, y, val) { var arr = new Array() for (var i = 0; i < x; i++) { arr.push(new Array()) for (var j = 0; j < y; j++) arr[i].push(val) } return arr } //显示矩阵信息 function showMat(g, rows, cols) { for (var i = 0; i < rows; i++) { for (var j = 0; j < cols; j++) console.log(g[i][j]) } } //重新绘制r,c图像,重置端点数组,矩阵数组 function reset(r, c) { $("#maze").remove() $('#cur_num').text('0') $('#all_num').text('0') x = parseInt(r) y = parseInt(c) //先拼接字符串,最后全部加入 str = "<div id='maze' style='margin-top: 5px'>" var btn_str = "<button class='uk-button uk-button-primary uk-button-small elem' style='height: 30px;width: 30px;margin-bottom: 1px;margin-right: 1px;background-color: lightgray' posx='{0}' posy='{1}'></button>" for (var i = 0; i < x; i++) { for (var j = 0; j < y; j++) { str += (btn_str.format(i, j)) } str += "<br>" } str += "</div>" $("#in").after(str) //阻止浏览器默认右键点击事件 //button元素的右击事件就被屏蔽了,而浏览器其他区域不受影响 $("button.elem").bind("contextmenu", function () { return false; }) //为所有的格子加上左右键单击事件 $("button.elem").each(function () { $(this).mousedown(function (e) { var x = parseInt($(this).attr('posx')) var y = parseInt($(this).attr('posy')) //右键为3 if (3 == e.which) { // alert('右键点击:' + x + "--" + y + "-" + color) right_click((this), x, y) } else if (1 == 3ff8 e.which) { //左键为1 // alert('左键键点击:' + x + "--" + y + "-" + color) left_click((this), x, y) } }) }); g = getMat(rows, cols, 1) ps = new Array() cur_num = 0 all_num = 0 paths = new Array() } //左键点击函数 function left_click(self, x, y) { if (g[x][y] == 1) { // var color = $(self).css("background-color") // alert(color) $(self).css("background-color", colors[0]) g[x][y] = 0 } else if (g[x][y] == 0) { $(self).css("background-color", colors[1]) g[x][y] = 1 } } //右键点击函数 function right_click(self, x, y) { if (g[x][y] == -1) { //是端点的话 g[x][y] = 1 $(self).css("background-color", colors[1]) if (ps.length == 1) { ps = new Array() } else { if (ps[0][1] == x && ps[0][2] == y) { ps.splice(0, 1) } else { ps.splice(1, 1) } } } else { //不是端点 g[x][y] = -1 $(self).css("background-color", colors[2]) ps.push([self, x, y]) // alert(ps.length) if (ps.length > 2) { g[ps[0][1]][ps[0][2]] = 1 $(ps[0][0]).css("background-color", colors[1]) ps.splice(0, 1) } // alert(ps.length) } //输出端点信息 // for (var i = 0; i < ps.length; i++) { // console.log(ps[i]) // } } //使用队列,返回一条路径数组 function getPath(g, sx, sy, ex, ey) { var path = new Array() var q = new Array() //0 表示可以访问 var vis = getMat(rows, cols, 0) vis[sx][sy] = 1 //保存格式 x,y,pre q.push([sx, sy, -1]) var front = 0 var tail = 1 while (front < tail) { var t = q[front] if (t[0] == ex && t[1] == ey) { var tt = t.slice() while (tt[2] != -1) { // 拼接函数(索引位置, 要删除元素的数量, 元素) path.splice(0, 0, tt.slice()); tt = q[tt[2]] } path.splice(0, 0, q[0]) return path } for (var i = 0; i < 4; i++) { var nx = t[0] + dir[i][0] var ny = t[1] + dir[i][1] if (nx >= 0 && ny >= 0 && nx < rows && ny < cols && g[nx][ny] != 0 && vis[nx][ny] == 0) { q.push([nx, ny, front]) vis[nx][ny] = 1 tail++ } } front += 1 } return path } //使用栈获取所有最短路 function getPaths(g, sx, sy, ex, ey) { max_len = getPath(g, sx, sy, ex, ey).length // alert(max_len) //0 表示可以走 var vis = getMat(rows, cols, 0) paths = new Array() var path = new Array() path.push([sx, sy, -1]) vis[sx][sy] = 1 //设置起点已经走过 while (path.length > 0) { //栈顶元素状态 i = path[path.length - 1][0] j = path[path.length - 1][1] d = path[path.length - 1][2] if (i == ex && j == ey) { //找到一条路径,此时栈中元素既是路径,深拷贝 paths.push(path.slice(0)) // alert('suc') //找到一 个元素后,退栈,并将目标点设为可以走,i,j,k,重新赋值 vis[ex][ey] = 0 //让终点可以走 path.splice(path.length - 1, 1) i = path[path.length - 1][0] j = path[path.length - 1][1] d = path[path.length - 1][2] } if (path.length > max_len) { //退栈 vis[path[path.length - 1][0]][path[path.length - 1][1]] = 0 path.splice(path.length - 1, 1) continue } find = 0 while (d < 3 && find == 0) { d++; i = path[path.length - 1][0] + dir[d][0] j = path[path.length - 1][1] + dir[d][1] if (i >= 0 && j >= 0 && i < rows && j < cols && g[i][j] != 0 && vis[i][j] == 0) { find = 1; } } if (find == 1) { //入栈 path[path.length - 1][2] = d; path.push([i, j, -1]) vis[i][j] = 1; } else { //退栈 vis[path[path.length - 1][0]][path[path.length - 1][1]] = 0 path.splice(path.length - 1, 1) } } return paths; } //使用dijkstra获取所有最短路径 pre = new Array() //是数组的数组 function dijkstra() { var cnt = rows * cols //是所有节点的数目 var d = new Array() //存放起始节点到其他节点的最短距离 var gg = getMat(cnt, cnt, -1) //-1表示两个点之间不能达到,非0数表示两点距离 var vis = new Array(cnt) var INF = 99999999 //构建新图 for (var i = 0; i < cnt; i++) { //将第i个节点变换为r,c的形式,找它所能够到达的所有格子 r = parseInt(i / cols) c = i % cols for (var j = 0; j < 4; j++) { var nx = r + dir[j][0] var ny = c + dir[j][1] if (nx >= 0 && ny >= 0 && nx < rows && ny < cols && g[nx][ny] != 0 && g[r][c] != 0) { var m = nx * cols + ny gg[i][m] = 1 //如果相邻两个点都不是障碍点,则两点距离为1 } } } //初始化 for (var i = 0; i < cnt; i++) { d[i] = INF //距离为无穷大 vis[i] = 0 //表示访问次数 } pre = new Array() for (var i = 0; i < cnt; i++) { pre.push(new Array()) pre[i].push(i) //每个节点至少指向自己 } var s = ps[0][1] * cols + ps[0][2] //起始节点的一维编号 // console.log('s = ' + s) d[s] = 0;//起点到自身的距离为0 for (var i = 0; i < cnt; i++) {//循环n次 var u = -1, mind = INF;//u使得d[u]最小,mind存放最小的d[u] for (var j = 0; j < cnt; j++) //找到未访问的顶点中d[]最小的 if (vis[j] == 0 && d[j] < mind) { u = j; mind = d[j]; } //找不到小于INF的d[j],说明剩下的顶点和起点s不联通 if (u == -1) return; vis[u] = 1;//标记u已经被访问 for (var v = 0; v < cnt; v++) { //如果v未访问 u能到达v 以u为中介点可以使得d[v]更小 if (vis[v] == 0 && gg[u][v] != -1 && d[u] + gg[u][v] < d[v]) { d[v] = d[u] + gg[u][v];//优化d[v] pre[v] = new Array() pre[v].push(u);//记录v的前驱顶点是u } else if (vis[v] == 0 && gg[u][v] != -1 && d[u] + gg[u][v] == d[v]) { pre[v].push(u);//记录v的前驱顶点是u } } } // console.log('cnt=' + cnt) // for (var i = 0; i < cnt; i++) { // console.log('gg' + i + ':' + gg[i]) // } // for (var i = 0; i < cnt; i++) { // console.log('pre' + i + ':' + pre[i]) // } // for (var i = 0; i < cnt; i++) { // console.log('d' + i + ':' + d[i]) // } } //dfs 由pre数组得到所有路径,s到aim的路径存放在paths中,每次需要初始化paths //需要使用path 也需要初始化 function dfs(s, aim, path) { for (var i = 0; i < pre[aim].length; i++) { if (pre[aim][i] == aim) continue path.push([parseInt(pre[aim][i] / cols), pre[aim][i] % cols]); if (pre[aim][i] == s) { paths.push(path.slice(0)); path.pop(); continue; } else { dfs(s, pre[aim][i], path); path.pop(); } } } //路径总数 all_path_cnt = 0 //最大路径数 max_path_cnt = 1000000 //计算路径总数,如果太多,则终止 function dfs_cnt(s, aim, path) { if (all_path_cnt >= max_path_cnt) return for (var i = 0; i < pre[aim].length; i++) { if (pre[aim][i] == aim) continue path.push([parseInt(pre[aim][i] / cols), pre[aim][i] % cols]); if (pre[aim][i] == s) { all_path_cnt++ path.pop(); continue; // console.log(all_path_cnt) } else { dfs_cnt(s, pre[aim][i], path); path.pop(); } } } //是否路径数目过多 function has_many_paths() { paths = new Array() dijkstra() all_path_cnt = 0 var s = ps[0][1] * cols + ps[0][2] var aim = ps[1][1] * cols + ps[1][2] var path = new Array() dfs_cnt(s, aim, path) console.log("all_path_cnt : " + all_path_cnt) return all_path_cnt >= max_path_cnt } //设置坐标为x,y的按钮颜色 function setColor(x, y, color) { //为所有的格子加上左右键单击事件 $("button.elem").each(function () { var posx = $(this).attr('posx') var posy = $(this).attr('posy') posx = parseInt(posx) posy = parseInt(posy) if (posx == x && posy == y) { $(this).css("background-color", color) } }); } //获得x,y坐标的btn引用 function getBtn(x, y) { $("button.elem").each(function () { var posx = $(this).attr('posx') var posy = $(this).attr('posy') posx = parseInt(posx) posy = parseInt(posy) // alert(posx + "-" + posy) if (posx == x && posy == y) { return $(this) } }); } //在图中显示路径path,是[x,y]坐标数组 function setPath(path) { if (path.length == 0) { alert('没有路径') return } //控制台输出路径信息 // for (var i = 0; i < path.length; i++) // console.log(path[i]) for (var i = 0; i < rows; i++) { for (var j = 0; j < cols; j++) { if (g[i][j] == 0) { setColor(i, j, colors[0]) } else if (g[i][j] == 1) { setColor(i, j, colors[1]) } else { setColor(i, j, colors[2]) } } } for (var i = 1; i < path.length - 1; i++) { setColor(path[i][0], path[i][1], colors[3]) } } function start() { if (ps.length != 2) { UIkit.notification("请选择两个端点", {pos: 'top-center', status: 'warning'}); return } var path = getPath(g, ps[0][1], ps[0][2], ps[1][1], ps[1][2]) if (path.length == 0) { $('#cur_num').text(0) $('#all_num').text(0) UIkit.notification("路径不存在...", {pos: 'bottom-center', status: 'warning'}); return } // console.log('rows=' + rows) // console.log('cols=' + cols) if (has_many_paths()) { UIkit.notification("路径过多无法计算,请增加障碍点或者减少路径长度...", {pos: 'bottom-center', status: 'warning'}); return } paths = new Array() //获取pre数组 dijkstra() var s = ps[0][1] * cols + ps[0][2] var aim = ps[1][1] * cols + ps[1][2] var path = new Array() path.push([parseInt(aim / cols), aim % cols]) // console.log(ps[0]) // console.log(ps[1]) // console.log(path[0]) // console.log('s=' + s) // console.log('aim=' + aim) //将pre数组转化为路径 dfs(s, aim, path) // console.log('paths.len=' + paths.length) // for (var i = 0; i < paths.length; i++) // console.log('paths=' + paths[i]) // alert(rows + '-' + cols) // paths = getPaths(g, ps[0][1], ps[0][2], ps[1][1], ps[1][2]) setPath(paths[0]) cur_num = 0 all_num = paths.length // alert('len=' + paths.length) // for (var i = 0; i < paths.length; i++) // alert(paths[i]) $('#cur_num').text(cur_num + 1) $('#all_num').text(paths.length) } $(document).ready(function () { rows = parseInt($("#form-rows").val()) cols = parseInt($("#form-cols").val()) reset(rows, cols) $("#reset").click(function () { reset(rows, cols) }); $("#start").click(function () { start() }); //行数获取 $("#form-rows").change(function () { // alert($(this).val()) rows = parseInt($("#form-rows").val()) cols = parseInt($("#form-cols").val()) reset(rows, cols) }); //列数获取 $("#form-cols").change(function () { // alert($(this).val()) rows = parseInt($("#form-rows").val()) cols = parseInt($("#form-cols").val()) reset(rows, cols) }); //上一页 $("#pre_btn").click(function () { // alert(paths.length) if (cur_num > 0) { cur_num-- 3ff0 setPath(paths[cur_num]) $('#cur_num').text(cur_num + 1) } }) //下一页 $("#next_btn").click(function () { // alert(paths.length) if (cur_num < paths.length - 1) { cur_num++ $('#cur_num').text(cur_num + 1) setPath(paths[cur_num]) } }) } ); </script> </head> <body class="uk-inline uk-container uk-container-large uk-position-center"> <h1 class="uk-heading-line uk-text-center uk-heading-divider"><span>迷宫寻路--阿豪</span></h1> <h5 class="uk-text-center"><span>左键设置障碍点,右键设置端点,路径总数最多支持1000000条</span></h5> <!-- 底层div --> <div class="uk-inline " style="vertical-align: middle;align-items: center;justify-content: center"> <!-- 输入div --> <div style="margin-bottom: 40px"> <form class="uk-form-horizontal "> <label class="uk-form-label" style="width: 50px;">行数</label> <div class="uk-form-controls" style="float: left;width: 50px;margin-right: 80px;margin-left:0px "> <select class="uk-select uk-form-small " id="form-rows"> <option>8</option> <option>10</option> <option>12</option> <option selected="selected">14</option> <option>16</option> <option>18</option> <option>20</option> </select> </div> <label class="uk-form-label " style="width: 50px">列数</label> <div class="uk-form-controls" style="float: left;width: 50px;margin-left: 0px"> <select class="uk-select uk-form-small" id="form-cols"> <option>8</option> <option>10</option> <option>12</option> <option selected="selected">14</option> <option>16</option> <option>18</option> <option>20</option> </select> </div> </form> </div> <!-- 按钮div --> <div id="in" style="vertical-align: middle;align-items:center;margin-left: 50px;"> <button id='reset' class="uk-button uk-button-primary uk-button-small" style="margin-right: 30px">重置迷宫</button> <button id='start' class="uk-button uk-button-primary uk-button-small">计算路径</button> </div> <div style="margin: 20px;vertical-align: middle;margin-left: 70px"> <button id='pre_btn' class="uk-button uk-button-default uk-button-small">上一条</button> <label id='cur_num' class="uk-form-label uk-small" style="width: 50px">0</label> / <label id='all_num' class="uk-form-label " style="width: 50px">0</label> <button id='next_btn' class="uk-button uk-button-default uk-button-small">下一条</button> </div> </div> </body> </html>
比较丑....
![](https://static.oschina.net/uploads/space/2017/0926/130510_npi1_2856757.png)
相关文章推荐
- 使用JQuery.js实现全选和反选
- 优秀js开源框架-jQuery使用手册(1)
- PHP、Smarty与jQuery Ajax 分页插件jquery.pager.js的使用
- ASPX多服务器控件下使用Jquery.validate.js
- 使用jquery的时候,js的window.onunload事件失效的解决办法
- jquery imgareaselect 使用利用js与程序结合实现图片剪切
- 优秀js开源框架-jQuery使用手册(2)
- 操作select的jquery插件 注意:使用时请保持JS文件的编码和你程序的编码一致...
- VS2010技巧:如何在js文件中使用jQuery智能感知
- jQuery 中插件的使用与开发-启用Visual Studio 对jQuery的智能感知(含 jQuery1.3.2 for VS 的智能提示js文件)
- 下面简单使用Jquery来操作iframe的一些记录,这个使用纯JS也可以实现。
- VS2010技巧:如何在js文件中使用jQuery智能感知
- IE6情况下使用jquery.bgiframe.js插件解决下拉列表框被遮盖BUG
- jquery.pager.js的使用
- jQuery 中插件的使用与开发---附全部源码(含 jQuery1.3.2 for VS 的智能提示js文件)
- 如何使用jquery对特殊字符进行转义,防止js注入
- JQuery.js学习(1)使用JQuery实现全选和反选
- jquery按需加载js和css插件使用说明
- 优秀js开源框架-jQuery使用手册(4)
- jQuery 中插件的使用与开发---附全部源码(含 jQuery1.3.2 for VS 的智能提示js文件)