拓扑排序算法主要过程:
1.创建一个数组记录每个节点的前驱节点数目。
2.运用递归,每一次递归中,找出没有前驱节点的节点,输出该节点,并做相关标记(或删去);然后找到该节点引出的连接,做相关标记(或删去)。
3.递归终止条件:所有的节点都已经被标记(或被删去),或者找不到没有前驱节点的节点。
核心代码如下:
sort = function(){
//找到没有前驱节点的节点
var len_links = links.length;
var arr_preNode = []; //存储左右节点的前驱节点数目
//初始化
for(var i = 0; i < 6; i++){
arr_preNode[i] = 0;
}
//执行拓扑排序的递归运算
doSort(len_links, arr_preNode);
//运行结束后清除文本框最后的"-->"符号
var textarea_sort = document.getElementById("textarea_sort");
textarea_sort.value = textarea_sort.value.substring(0, textarea_sort.value.length-3);
}
//开始拓扑排序的递归运算
function doSort(len_links, arr_preNode){
//将没有被标记的arr_preNode的元素初始化
for(var i = 0; i < arr_preNode.length; i++){
if(arr_preNode[i] != "done"){
arr_preNode[i] = 0;
}
}
for(var i = 0; i < len_links; i++){
if(links[i].pro != "0"){
if(arr_preNode[parseInt(links[i].target)-1] != "done"){
arr_preNode[parseInt(links[i].target)-1] ++;
}
}
}
//找到一个没有前驱节点的节点
var node_index = null;
for(var i = 0; i < len_links; i++){
if(arr_preNode[i] == 0){
node_index = i;
break;
}
}
//向文本框中输出结果
var textarea_sort = document.getElementById("textarea_sort");
textarea_sort.value += dataVisible[node_index].value[2] + "-->";
//标记该节点
arr_preNode[node_index] = "done";
//标记该节点引出的相关连接
for(var i = 0; i < len_links; i++){
if(links[i].source == (node_index+1+"")){
links[i].pro = "0";
}
}
console.log("arr_preNode:"+ arr_preNode);
console.log("links:"+ links);
//终止递归的条件:arr_preNode中所有的元素都被标记
for(var i = 0; i < arr_preNode.length; i++){
if(arr_preNode[i] != "done"){
doSort(len_links, arr_preNode);
}
}
return;
}
完整代码如下:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<script src="echarts.min.js"></script>
<title></title>
<style>
body{
padding:0;
margin:0;
background-color:#F9F9F9;
}
*{
padding:0;
margin:0;
background-color:#F9F9F9;
}
.textarea_wrapper{
position: relative;
left: 0;
top: 150px;
height: 300px;
width: 300px;
}
.textarea_sort{
display: block;
position: absolute;
bottom: 0;
width: 100%;
height: 260px;
box-sizing: border-box;
color: #6d757a;
font-size: 15px;
font-family: Microsoft Yahei;
font-weight: 600;
line-height: 20px;
padding: 10px;
border: 1px solid #ddd;
border-radius: 4px;
outline: none;
resize: none;
transition: all .3s ease;
cursor: text;
white-space: pre-wrap;
overflow-wrap: break-word;
word-spacing: normal;
text-transform: none;
text-indent: 0px;
text-shadow: none;
text-rendering: auto;
}
.button{
position: absolute;
color: #fff;
top: 0;
font-size: 15px;
font-weight: 600;
height: 30px;
width: 200px;
line-height:30px;
background: #1E90FF;
text-align: center;
border:1px solid blue;
border-radius: 5px;
cursor: pointer;
margin-left: 46px;
}
#mainEcharts{
position: fixed !important;
height: 600px;
width: 400px;
top:50%;
left:50%;
transform: translate3d(-50%,-50%,0);
}
.alert{
position: fixed;
margin: 0 auto;
line-height: 30px;
font-size: 20px;
font-weight: 600;
color: red;
left: 50%;
top:0;
transform: translate3d(-50%,0,0);
}
</style>
</head>
<body>
<div class="alert">
1.所有节点均可拖拽;<br>
2.推荐使用chrome/firefox浏览器。<br>
</div>
<div id="mainEcharts"></div>
<div class="textarea_wrapper">
<div class="button button_sort" onclick="sort()">Click me-->拓扑排序</div>
<textarea class="textarea_sort" id="textarea_sort" cols="30" rows="10"></textarea>
</div>
</body>
<script type="text/javascript">
var dataVisible = [];//存放图中各个节点的数据信息
var links = [];//存放图中各个连接
var dataChild1=[{
name:"1",//节点id
symbol:"circle",//节点形状
symbolSize:75,//节点大小
//[x坐标,y坐标,label,颜色]
value: [100, 100,"1","#1E90FF"],
}];
var dataChild2=[{
name:"2",//节点id
symbol:"circle",//节点形状
symbolSize:75,//节点大小
//[x坐标,y坐标,label,颜色]
value: [500, 100,"2","#1E90FF"],
}];
var dataChild3=[{
name:"3",//节点id
symbol:"circle",//节点形状
symbolSize:75,//节点大小
//[x坐标,y坐标,label,颜色]
value: [500, 400,"3","#1E90FF"],
}];
var dataChild4=[{
name:"4",//节点id
symbol:"circle",//节点形状
symbolSize:75,//节点大小
//[x坐标,y坐标,label,颜色]
value: [100, 400,"4","#1E90FF"],
}];
var dataChild5=[{
name:"5",//节点id
symbol:"circle",//节点形状
symbolSize:75,//节点大小
//[x坐标,y坐标,label,颜色]
value: [500, 800,"5","#1E90FF"],
}];
var dataChild6=[{
name:"6",//节点id
symbol:"circle",//节点形状
symbolSize:75,//节点大小
//[x坐标,y坐标,label,颜色]
value: [100, 800,"6","#1E90FF"],
}];
//将所有节点存入dataVisible中
dataVisible = dataVisible.concat(dataChild1)
.concat(dataChild2)
.concat(dataChild3)
.concat(dataChild4)
.concat(dataChild5)
.concat(dataChild6);
var linkChild12 = [{
source: "1",
target: "2",
pro: "1",
label:{
normal:{
show: false,
}
},
lineStyle:{
normal:{
width: 2,
color: '#8B0000',
}
}
}];
var linkChild14 = [{
source: "1",
target: "4",
pro: "1",
label:{
normal:{
show: false,
}
},
lineStyle:{
normal:{
width: 2,
color: '#8B0000',
}
}
}];
var linkChild13 = [{
source: "1",
target: "3",
pro: "1",
label:{
normal:{
show: false,
}
},
lineStyle:{
normal:{
width: 2,
color: '#8B0000',
}
}
}];
var linkChild32 = [{
source: "3",
target: "2",
pro: "1",
label:{
normal:{
show: false,
}
},
lineStyle:{
normal:{
width: 2,
color: '#8B0000',
}
}
}];
var linkChild35 = [{
source: "3",
target: "5",
pro: "1",
label:{
normal:{
show: false,
}
},
lineStyle:{
normal:{
width: 2,
color: '#8B0000',
}
}
}];
var linkChild45 = [{
source: "4",
target: "5",
pro: "1",
label:{
normal:{
show: false,
}
},
lineStyle:{
normal:{
width: 2,
color: '#8B0000',
}
}
}];
var linkChild64 = [{
source: "6",
target: "4",
pro: "1",
label:{
normal:{
show: false,
}
},
lineStyle:{
normal:{
width: 2,
color: '#8B0000',
}
}
}];
var linkChild65 = [{
source: "6",
target: "5",
pro: "1",
label:{
normal:{
show: false,
}
},
lineStyle:{
normal:{
width: 2,
color: '#8B0000',
}
}
}];
links = links.concat(linkChild12)
.concat(linkChild13)
.concat(linkChild14)
.concat(linkChild32)
.concat(linkChild35)
.concat(linkChild45)
.concat(linkChild64)
.concat(linkChild65);
var dataVisible_p=[];
for(var i=0;i<dataVisible.length;i++){
dataVisible_p[i]=new Array();
for(var j=0;j<2;j++){
dataVisible_p[i][j]=dataVisible[i].value[j];
}
}
//利用echarts框架令数据可视化
var myChart=echarts.init(document.getElementById("mainEcharts"));
myChart.setOption({
xAxis: {
min: 0,
max: 1000,
position: 'top',
type: 'value',
show:false,
axisLine: {
onZero: true,
lineStyle:{
color:'#DCDCDC'
}
},
axisLabel:{show:true},
axisTick:{show:true},
},
yAxis: {
min: 0,
max: 800,
inverse: true,
type: 'value',
show:false,
axisLine: {
onZero: false,
lineStyle:{
color:'#DCDCDC'
}
},
axisLabel:{show:true},
axisTick:{show:true},
color:'#DCDCDC'
},
series: [
{
id: 'dataVisible',
type: 'graph',
layout: 'none',
coordinateSystem: 'cartesian2d',
smooth: true,
data:dataVisible,
itemStyle: {
normal: {
color:function (parmas) {
return dataVisible[parmas.dataIndex].value[3];
},
borderColor: '#1E90FF',
borderWidth: 2,
shadowBlur: 10,
shadowColor: '#82dffe',
}
},
label: {
normal: {
position: 'inside',
show: true,
formatter: function (parmas) {
return dataVisible[parmas.dataIndex].value[2];
},
backgroundColor: '#eee',
borderColor: '#555',
borderWidth: 2,
borderRadius: 5,
padding: 10,
fontSize: 18,
textBorderColor: '#000',
textBorderWidth: 3,
color: '#fff'
}
},
edgeSymbol: ['circle', 'arrow'],
edgeSymbolSize: [10, 20],
links: links
},
],
});
//添加节点可拖动功能
myChart.setOption({
series: [{
id: 'dataVisible',
data: dataVisible,
}],
graphic: echarts.util.map(dataVisible_p, function (item, dataIndex) {
return {
id:"dataVisibleCircle"+dataIndex,
type: 'circle',
position: myChart.convertToPixel('grid', item),
shape: {
r: dataVisible[dataIndex].symbolSize/2+dataVisible[dataIndex].symbolSize/8
},
invisible: true,
draggable: true,
ondrag: echarts.util.curry(ondrag, dataIndex),
z: 100
};
})
});
//拖动节点
function ondrag(dataIndex){
var myChart=echarts.init(document.getElementById("mainEcharts"));
dataVisible_p[dataIndex] = myChart.convertFromPixel('grid', this.position);
var x = dataVisible_p[dataIndex][0];
var y = dataVisible_p[dataIndex][1];
dataVisible[dataIndex].value[0] = x;
dataVisible[dataIndex].value[1] = y;
myChart.setOption({
series: [{
id: 'dataVisible',
data: dataVisible,
}]
});
}
//进行拓扑排序
sort = function(){
//找到没有前驱节点的节点
var len_links = links.length;
var arr_preNode = []; //存储左右节点的前驱节点数目
//初始化
for(var i = 0; i < 6; i++){
arr_preNode[i] = 0;
}
//执行拓扑排序的递归算法
doSort(len_links, arr_preNode);
//运行结束后清除文本框最后的"-->"符号
var textarea_sort = document.getElementById("textarea_sort");
textarea_sort.value = textarea_sort.value.substring(0, textarea_sort.value.length-3);
}
//进行拓扑排序的递归算法
function doSort(len_links, arr_preNode){
//将没有被标记的arr_preNode的元素初始化
for(var i = 0; i < arr_preNode.length; i++){
if(arr_preNode[i] != "done"){
arr_preNode[i] = 0;
}
}
for(var i = 0; i < len_links; i++){
if(links[i].pro != "0"){
if(arr_preNode[parseInt(links[i].target)-1] != "done"){
arr_preNode[parseInt(links[i].target)-1] ++;
}
}
}
//找到一个没有前驱节点的节点
var node_index = null;
for(var i = 0; i < len_links; i++){
if(arr_preNode[i] == 0){
node_index = i;
break;
}
}
//向文本框中输出结果
var textarea_sort = document.getElementById("textarea_sort");
textarea_sort.value += dataVisible[node_index].value[2] + "-->";
//标记该节点
arr_preNode[node_index] = "done";
//标记该节点引出的相关连接
for(var i = 0; i < len_links; i++){
if(links[i].source == (node_index+1+"")){
links[i].pro = "0";
}
}
console.log("arr_preNode:"+ arr_preNode);
console.log("links:"+ links);
//终止递归的条件:arr_preNode中所有的元素都被标记
for(var i = 0; i < arr_preNode.length; i++){
if(arr_preNode[i] != "done"){
doSort(len_links, arr_preNode);
}
}
return;
}
</script>
</html>