因此,我想出了一个有趣的树形结构,覆盖了所有向下延伸的可能性,不仅仅是2层,层数越多,树形结构就越复杂。请看这个3层的示例,您在中间看到的三角形代表一个物品,周围的其他彩色形状代表它达到下一级的可能配对项:
(图片已省略)
有很多不同的组合方式,我可以先将我的物品与蓝色的物品组合,然后与红色的物品组合,然后再与绿色的物品组合,或者先将其与绿色的物品组合,然后再与红色的物品组合,然后再与蓝色的物品组合等等,以达到最终的级别。我想出了这个树形结构,表示所有可能的组合:
(图片已省略)
(右侧数字是#levels,左侧是每个级别的#nodes)但是,正如您所看到的,这是冗余的。如果您查看末端节点,它们应该全部为1,因为它们都指向相同的最终结果,即G+R+B。实际上,对于这种情况,总共有7种可能的状态,这是正确的树形结构:
(图片已省略)
这非常有意义,请注意节点数量的巨大差异。
现在我的问题是,对于这个问题什么是正确的数据结构? - 我相当确定没有内置的数据结构适用于此,因此我需要制作自己的自定义数据结构,我实际上已经做到了,并设法使其正常工作,但出现了一个问题。(值得一提的是,我从XML文件中获取节点信息,这些信息包括到达节点/级别所需的itemRequired以及该节点的名称是什么,例如:要达到RedGreenHerb状态,绿色草需要“需要”红色草,当这种组合发生时,“GreenHerb”的名称将更改为“RedGreenHerb”,如果你想知道“RedHerb”会发生什么,它只是消失了,我不再需要它)这是我的数据结构:
public struct TransData
{
public TransData(string transItemName, string itemRequired)
{
this.transItemName = transItemName;
this.itemRequired = itemRequired;
}
public string transItemName;
public string itemRequired;
}
public class TransNode
{
public List<TransNode> nodes = new List<TransNode>();
public TransData data;
public TransNode(TransNode node): this(node.data.transItemName, node.data.itemRequired) { }
public TransNode(string itemName, string itemRequired)
{
data = new TransData(itemName, itemRequired);
}
}
public class TransLevel
{
public List<TransNode> nodes = new List<TransNode>();
public TransNode NextNode { get { return nodes[cnt++ % nodes.Count]; } }
int cnt;
}
public class TransTree
{
public TransTree(string itemName)
{
this.itemName = itemName;
}
public string itemName;
public TransNode[] nodes;
public List <TransLevel> levels = new List<TransLevel>();
// other stuff...
}
让我来解释一下:
TransTree
实际上是基本节点,起始处有项目名称(例如 GreenHerb),该树有多个级别(您在图片中看到的黑线),每个级别有多个节点,每个节点都携带一个新的项目数据和指向其他节点(子节点)的节点数。现在您可能会问为什么需要在 TransTree
类中放置节点列表?- 在展示如何从 XML 文件中获取数据之后,我将回答这个问题:public TransTree GetTransItemData(string itemName)
{
var doc = new XmlDocument();
var tree = new TransTree(itemName);
doc.LoadXml(databasePath.text);
var itemNode = doc.DocumentElement.ChildNodes[GetIndex(itemName)];
int nLevels = itemNode.ChildNodes.Count;
for (int i = 0; i < nLevels; i++) {
var levelNode = itemNode.ChildNodes[i];
tree.levels.Add(new TransLevel());
int nPaths = levelNode.ChildNodes.Count;
for (int j = 0; j < nPaths; j++) {
var pathNode = levelNode.ChildNodes[j];
string newName = pathNode.SelectSingleNode("NewName").InnerText;
string itemRequired = pathNode.SelectSingleNode("ItemRequired").InnerText;
tree.levels[i].nodes.Add(new TransNode(newName, itemRequired));
}
}
tree.ConnectNodes(); // pretend these two
tree.RemoveLevels(); // lines don't exist for now
return tree;
这里是一个XML示例,以便更清晰地说明:
ItemName -> Level -> Path(即节点)-> Path数据。
<IOUTransformableItemsDatabaseManager>
<GreenHerb>
<Level_0>
<Path_0>
<NewName>RedGreenHerb</NewName>
<ItemRequired>RedHerb</ItemRequired>
</Path_0>
<Path_1>
<NewName>BlueGreenHerb</NewName>
<ItemRequired>BlueHerb</ItemRequired>
</Path_1>
</Level_0>
<Level_1>
<Path_0>
<NewName>GreyHerb</NewName>
<ItemRequired>BlueHerb</ItemRequired>
</Path_0>
</Level_1>
</GreenHerb>
</IOUTransformableItemsDatabaseManager>
现在这样做的问题在于,节点之间没有连接,那么这意味着什么?如果一个项目需要走到某个级别,那么我们肯定不需要继续存储它没有走过的其他路径,为什么要把它们保留在内存中呢?(一旦你选择了一条路径,就不能回头了,必须一直沿着这条路走下去)我希望的是,当我取出一个节点时,所有其下的节点也会随之下降,这是有意义的,但目前我所做的方式类似于这样:
tree.ConnectNodes();
tree.RemoveLevels();
我首先连接节点,然后再移除层级?为什么?因为如果我不这样做,那么每个节点都有两个引用,一个来自其父节点,另一个来自当前层级。
现在ConnectNode
实际上是针对我展示的长树,而不是优化后只有7个状态的树:
// this is an overloaded version I use inside a for loop in ConnectNodes()
private void ConnectNodes(int level1, int level2)
{
int level1_nNodes = levels[level1].nodes.Count;
int level2_nNodes = levels[level2].nodes.Count;
// the result of the division gives us the number of nodes in level2,
// that should connect to each node in level1. 12/4 = 3, means that each
// node from level1 will connect to 3 nodes from level2;
int nNdsToAtch = level2_nNodes / level1_nNodes;
for (int i = 0, j = 0; i < level2_nNodes; j++)
{
var level1_nextNode = levels[level1].nodes[j];
for (int cnt = 0; cnt < nNdsToAtch; cnt++, i++)
{
var level2_nextNode = levels[level2].nodes[i];
level1_nextNode.nodes.Add(new TransNode(level2_nextNode));
}
}
}
这就是我想要在另一棵树上实现的目标,但我不知道怎么做。我想连接节点并形成我展示的第二棵树,相对于四层来说它更加简单,我甚至无法在绘图中连接节点!(当我尝试四层时)如果你仔细观察,你会发现它有些类似于二进制数字,以下是我的树的二进制形式:
001
010
100
011
101
110
111
每个‘1’代表一个实际物品,‘0’表示为空。在我的树中,'001'=蓝色,'010'=绿色,'100'=红色,'011'表示绿色+蓝色,... '111'=灰色(最终层级)。
现在我已经解释清楚了一切,首先:我的方法正确吗?如果不是,那么是什么呢? 如果是这样,那么我可以使用/制作哪种数据结构来实现?如果我想到的数据结构放在它们的位置上,如何将XML文件中的数据以一种连接节点的方式存储到我的数据结构中,以便每当我取出一个节点时,它会连同其子节点一起取出?
非常感谢您的帮助和耐心 :)
编辑:有趣的是,整个系统只用于游戏中仅出现一次的物品(被拾取一次)。这就是为什么每当我走一条路时,我会从内存中删除它,并且每当我拿起一个物品时,我会从数据库中删除它的条目,因为我不会再遇到它。
编辑:请注意,我不仅使用字符串来表示我的物品,它们还有很多其他属性。但在这种情况下,我只关心它们的名称,这就是为什么我处理字符串的原因。