遍历以多种方式相互连接的对象列表

时间:2014-04-29 10:08:04

标签: javascript arrays graph

我有一个充满对象的数组,其中每个对象都有"退出"导致数组中的其他对象。我想知道如何遍历这些项目"关注"每个出口。

基本思路是创建一个" map"数组中的对象。

基本上,每个对象都是(减少只显示重要部分):

{
    id: 0,
    name: "stary sobor",
    n: -1,
    s: 3,
    e: 1,
    w: -1,
    u: -1,
    d: -1,
}

基本上,我希望算法能够遵循大于-1的任何方向(n,s,e,w,u,d)。

我已经尝试将其映射出来,但是当对象有多个退出时,我对非常感到困惑。

这是我的小提琴:http://jsfiddle.net/8kxrp/2/

基本上我想遍历数组并在画布上为每个区域输出一个正方形 - 所以,如果area1有一个"退出"那么我想在area1等的右边显示一个正方形。

3 个答案:

答案 0 :(得分:2)

您正在遍历图表。这里的关键点是:

  1. 你可以通过多种方式到达某个“房间”;因此,您需要跟踪您已经访问过的房间,这样您就不会再次尝试关注他们的出口。
  2. 由于每个房间都有多个出口,因此您需要及时访问其中一个出口,同时记得回来后再尝试其他房间。
  3. 一种简单的方法是使用递归来访问所有出口,同时在已经访问过的房间中存储一些标志:

    var visitRoom = function(rooms, roomId) {
      if (roomId === -1)
        return;
    
      var room = rooms[roomId];
      if (room.visited)
        return;
    
      room.visited = true;
      doSomethingWithRoom(room);
    
      visitRoom(rooms, room.n);
      visitRoom(rooms, room.e);
      visitRoom(rooms, room.s);
      visitRoom(rooms, room.w);
      visitRoom(rooms, room.u);
      visitRoom(rooms, room.d);  
    };
    
    visitRoom(rooms, 0); // assuming you're starting at the first room
    

    这里的问题是:

    • 存储room.visited有点乱,因为它会以遍历算法所需的中间状态污染原始数据。

    • 此代码假设每个房间的ID等于其在数组中的索引。如果不是这种情况,您可能希望找到一种通过其ID获取房间的方法。房间ID和房间对象之间的地图在这种情况下比数组更有用。

    • 递归方法会耗尽大量映射中的堆栈内存。这可以通过使用迭代方法来补救,而不是通过递归访问房间,而是将其ID添加到要访问的房间列表中,然后连续访问列表中的第一个房间,直到它为空。保留未访问的房间列表比为递归调用存储堆栈帧花费更少的内存。

    这是解决这些问题的另一种方法:

    var visitAll = function(rooms) {
      var toVisit = [0];
      var visited = {};
    
      while (toVisit.length > 0) {
        var roomId = toVisit.pop();
    
        var room = findRoomById(rooms, roomId); 
    
        doSomethingWithRoom(room);
        visited[roomId] = true;
    
        ["n", "s", "e", "w", "u", "d"].forEach(function(exit) {
          var exitId = room[exit];
          if (exitId !== -1 && !visited[exitId])
            toVisit.push(exitId);
        });    
      }
    }
    
    var findRoomById = function(rooms, roomId) {
      // do whatever you need to find the room with the correct ID
      return rooms[roomId]; 
    }
    
    var doSomethingWithRoom = function(room) {
      // do what you want here; this will be called once per visited room
    }
    
    visitAll(rooms);
    

    如果您还想跟踪每个房间的空间坐标,您也可以根据前一个房间的坐标和出口的方向,在每个房间第一次遇到房间时为其指定位置。紧随其后。

答案 1 :(得分:0)

这实际上取决于您希望如何处理多个退出。

var dirs = ["n", "e", "s", "w", "u", "d"];
var visited = {};
for(var i = 0; i < Areas.length; i++){
  for (var j = 0; j < dirs.length; j++) {
    if(Areas[i][dirs[j]] > -1){
      doSomething(array[i].id, dirs[j], array[i][dirs[j]);
    }
  };
}

function doSomething(roomID, exitDirection, exitID){

  //Called for every exit for every room.

  if(visited[exitID]!== true){
    visited[exitID] = true;

    //Do whatever unique stuff you want to do here.
    //This will get called for every new exit room but you may not want that if you're building doors or something.
  }      
}

答案 2 :(得分:0)

http://jsfiddle.net/8kxrp/5/

这将为每个元素增加一个&#34;其他&#34; field,带有所有可到达元素的结构化多维对象,以及带有可到达元素的简单列表的allothers数组。

var Chernarus = [
    {
        id: 0,
        name: "stary sobor",
        description: "",
        smell: "",
        objects: [],
        items: [],
        clothes: [],
        food: [],
        barricades: [],
        n:-1,s:3,e:1,w:-1,
        u:-1,d:-1,
        z:0,
        bg:"img/centre.jpg"
    },{
        id: 1,
        name: "novy sobor",
        description: "",
        smell: "",
        objects: [],
        items: [],
        clothes: [],
        food: [],
        barricades: [],
        n:-1,s:2,e:-1,w:0,
        u:-1,d:-1,
        z:0,
        bg:"img/centre.jpg"
    },{
        id: 2,
        name: "mogilevka",
        description: "",
        smell: "",
        objects: [],
        items: [],
        clothes: [],
        food: [],
        barricades: [],
        n:1,s:3,e:-1,w:-1,
        u:-1,d:-1,
        z:0,
        bg:"img/centre.jpg"
    },{
        id: 3,
        name: "dubky",
        description: "",
        smell: "",
        objects: [],
        items: [],
        clothes: [],
        food: [],
        barricades: [],
        n:2,s:4,e:-1,w:-1,
        u:-1,d:-1,
        z:0,
        bg:"img/centre.jpg"
    },{
        id: 4,
        name: "novo selky",
        description: "",
        smell: "",
        objects: [],
        items: [],
        clothes: [],
        food: [],
        barricades: [],
        n:3,s:5,e:-1,w:-1,
        u:-1,d:-1,
        z:0,
        bg:"img/centre.jpg"
    },{
        id: 5,
        name: "chernogorsk",
        description: "",
        smell: "",
        objects: [],
        items: [],
        clothes: [],
        food: [],
        barricades: [],
        n:4,s:-1,e:-1,w:6,
        u:-1,d:-1,
        z:0,
        bg:"img/centre.jpg"
    }
];

var toCheck=["n","s","e","w"];
var maxSteps=100;

function getOthers(element,recursion,alreadyDone,alreadyArr) {

    if(recursion>maxSteps) return "TOO MANY:INFINITE?";
    if(Chernarus[element]==undefined)  return "NOT FOUND: "+element;
    var item=Chernarus[element];
    if(alreadyDone[element]!=undefined) return element+" ALREADY DONE";
    alreadyDone[element]=true;
    alreadyArr.push(element);
    var out={};
    for (var ch=0;ch<4;ch++) {
        var tc=toCheck[ch];
        var dir=item[toCheck[ch]];
        if(dir>0) {
            out[tc]={pos:dir,others:getOthers(dir,recursion+1,alreadyDone,alreadyArr)};
        }
    }
    return out;
}


for (var i=0;i<Chernarus.length;i++) {
    var allothers=[]
    Chernarus[i]['others']=getOthers(i,1,[],allothers);
    Chernarus[i]['allothers']=allothers;
}







alert(dump(Chernarus,""));

function dump(obj,spc) {
    if(obj==undefined) return "<undefined>";
    if((typeof obj)=="object") { // obj instanceof Array || obj.toString()=="[object Object]"
        var out="";
        for(var it in obj) if(obj.hasOwnProperty(it)) {
            out+=spc+it+"= {\n";
            out+= dump(obj[it],spc+"    ");
            out+=spc+"}\n";
        }
        return out;
    } else return spc+(obj.toString())+"\n";
}