递归创建带有类 C++ 的链表
我正在使用 C++ 递归地创建一个六边形网格(使用多重链接列表样式)。我已将其设置为轻松创建相邻图块,但因为我是递归执行的,所以我只能为给定图块真正创建所有 6 个相邻图块。显然,这会导致创建重复的图块,我正在尝试以某种方式摆脱它们。因为我正在使用一个类,所以检查空指针似乎不起作用。它要么无法从我的 Tile 类转换为 int,要么以某种方式转换它但没有正确执行。我在创建时明确将所有指针设置为 NULL,当我检查它是否仍然是 NULL 时,它说它不是,即使我自初始化以来从未碰过它。我应该有一个具体的方法来做到这一点吗?如果没有某种 NULL,我什至无法遍历网格
这是我的一些相关代码。是的,我知道这很尴尬。
图块类头:
class Tile
{
public:
Tile(void);
Tile(char *Filename);
~Tile(void);
void show(void);
bool LoadGLTextures();
void makeDisplayList();
void BindTexture();
void setFilename(char *newName);
char Filename[100];
GLuint texture[2];
GLuint displayList;
Tile *neighbor[6];
float xPos, yPos,zPos;
};`
图块初始化:
Tile::Tile(void)
{
xPos=0.0f;
yPos=0.0f;
zPos=0.0f;
glEnable(GL_DEPTH_TEST);
strcpy(Filename, strcpy(Filename, "Data/BlueTile.bmp"));
if(!BuildTexture(Filename, texture[0]))
MessageBox(NULL,"Texture failed to load!","Crap!",MB_OK|MB_ICONASTERISK);
for(int x=0;x<6;x++)
{
neighbor[x]=NULL;
}
}
创建相邻图块:
void MakeNeighbors(Tile *InputTile, int stacks)
{
for(int x=0;x<6;x++)
{
InputTile->neighbor[x]=new Tile();
InputTile->neighbor[x]->xPos=0.0f;
InputTile->neighbor[x]->yPos=0.0f;
InputTile->zPos=float(stacks);
}
if(stacks)
{
for(int x=0;x<6;x++)
MakeNeighbors(InputTile->neighbor[x],stacks-1);
}
}
最后,遍历网格:
void TraverseGrid(Tile *inputTile)
{
Tile *temp;
for(int x=0;x<6;x++)
if(inputTile->neighbor[x])
{
temp=inputTile->neighbor[x];
temp->xPos=0.0f;
TraverseGrid(temp);
//MessageBox(NULL,"Not Null!","SHUTDOWN ERROR",MB_OK | MB_ICONINFORMATION);
}
}
关键行是“if(inputTile->neighbor[x])”以及我是否将其设为“if(inputTile->neighbor[ x]==NULL)" 或无论我做什么,它只是没有正确处理它。哦,我也知道我还没有完全设置列表。现在只有一个方向。
I'm using C++ to recursively make a hexagonal grid (using a multiply linked list style). I've got it set up to create neighboring tiles easily, but because I'm doing it recursively, I can only really create all 6 neighbors for a given tile. Obviously, this is causing duplicate tiles to be created and I'm trying to get rid of them in some way. Because I'm using a class, checking for null pointers doesn't seem to work. It's either failing to convert from my Tile class to and int, or somehow converting it but not doing it properly. I'm explicitly setting all pointers to NULL upon creation, and when I check to see if it still is, it says it's not even though I never touched it since initialization. Is there a specific way I'm supposed to do this? I can't even traverse the grid without NULLs of some kind
Here's some of my relevant code. Yes, I know it's embarassing.
Tile class header:
class Tile
{
public:
Tile(void);
Tile(char *Filename);
~Tile(void);
void show(void);
bool LoadGLTextures();
void makeDisplayList();
void BindTexture();
void setFilename(char *newName);
char Filename[100];
GLuint texture[2];
GLuint displayList;
Tile *neighbor[6];
float xPos, yPos,zPos;
};`
Tile Initialization:
Tile::Tile(void)
{
xPos=0.0f;
yPos=0.0f;
zPos=0.0f;
glEnable(GL_DEPTH_TEST);
strcpy(Filename, strcpy(Filename, "Data/BlueTile.bmp"));
if(!BuildTexture(Filename, texture[0]))
MessageBox(NULL,"Texture failed to load!","Crap!",MB_OK|MB_ICONASTERISK);
for(int x=0;x<6;x++)
{
neighbor[x]=NULL;
}
}
Creation of neighboring tiles:
void MakeNeighbors(Tile *InputTile, int stacks)
{
for(int x=0;x<6;x++)
{
InputTile->neighbor[x]=new Tile();
InputTile->neighbor[x]->xPos=0.0f;
InputTile->neighbor[x]->yPos=0.0f;
InputTile->zPos=float(stacks);
}
if(stacks)
{
for(int x=0;x<6;x++)
MakeNeighbors(InputTile->neighbor[x],stacks-1);
}
}
And finally, traversing the grid:
void TraverseGrid(Tile *inputTile)
{
Tile *temp;
for(int x=0;x<6;x++)
if(inputTile->neighbor[x])
{
temp=inputTile->neighbor[x];
temp->xPos=0.0f;
TraverseGrid(temp);
//MessageBox(NULL,"Not Null!","SHUTDOWN ERROR",MB_OK | MB_ICONINFORMATION);
}
}
The key line is "if(inputTile->neighbor[x])" and whether I make it "if(inputTile->neighbor[x]==NULL)" or whatever I do, it just isn't handling it properly. Oh and I'm also aware that I haven't set up the list fully. It's only one direction now.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(5)
如果您想创建六角形网格,您应该记住,可以使用普通网格轻松模拟它!
这将使生活变得更加简单:)
因此,最简单的方法是
m*n
现在,根据上图进行连接:
...并且您拥有所有所需的连接(只需记住检查边界以确定图块是否不在边缘) - 如果您以其他方式握住图块,您甚至可以删除网格:)。
If you want to create a hexagonal grid you should remember that it easily can be simulated using a normal grid!
This will make life MUCH simpler :)
Hence the easiest way would be
m*n
Now the connections, based on the diagram above:
... and you have all desired connections (just remember to check bounds to decide if the tile isn't on the edge) -- if you hold the tiles in another way, you can even remove the grid :).
我只是猜测 MakeNeighbors() 的作用,但不要盲目地执行
InputTile->neighbor[x]=new Tile();
,您可以检查一下 neighbor[x] 是否是在创建新的并初始化它之前非 NULL。例如,如果它的父级创建了它并设置了它的所有邻居信息,那么它不应该去创建它的父级。当父级创建子级时,它还应该适当地定义子级的其他邻居(只要它知道)。因此,它应该确保 child[i] 也是 child[i-1] 和 child[i+1] 的邻居。
I'm only guessing at what MakeNeighbors() does, but instead of blindly doing
InputTile->neighbor[x]=new Tile();
, you could check to see if neighbor[x] is non-NULL before creating a new one and initializing it. E.g. if its parent creates it and sets all of its neighbor information, then it shouldn't go and create its parent.When the parent creates the children, it should also define the children's other neighbors appropriately, as far as it knows them. So, it should make sure that child[i] also is neighbors with child[i-1] and child[i+1].
创造。递归是解决某些问题的一种简洁而优雅的方法,但它并不适合所有问题。我怀疑创建节点的纯粹递归解决方案会比 Kornel Kisielewicz 的简单迭代解决方案。这是因为
Tile
构造函数需要知道其附近所有图块的布局,以避免重新创建已经存在的节点。遍历。节点遍历代码中的主要问题是相似的,因为每个节点最终都会“遍历”回其父节点,开始循环,因此您将陷入无限循环并破坏堆栈再次。我想您正试图访问每个图块一次,对吧?在这种情况下,
TraverseGrid()
需要有一个参数告诉它我们从哪个方向进入节点,这样我们就可以避免从那个方向遍历回来。但这还不够——你还需要更多的纪律来决定前进的方向。简单地向除我们输入的方向之外的所有方向展开仍然会陷入无限循环和堆栈溢出,因为任何三个相邻的图块都会无限循环。为了递归地执行此操作,您需要真正考虑哪些策略将最终访问每个节点一次且仅一次。
一种可能是将
TraverseGrid()
的签名更改为TraverseGrid(Tile *inputTile, int fromDir, bool leftmost)
,然后使用以下规则:leftmost = false
。leftmost = false
。leftmost
,并且左下角有一个节点,那么也遍历到该节点,传递leftmost = true
。当然,
fromDir
和leftmost
可以组合成一个参数,但这给出了总体思路。另一种选择是在每个图块中保留一个
visited
标志,在遍历该图块之前检查该标志。那么你的遍历将是洪水填充。但同样,简单的迭代遍历可能会简单得多且更容易理解,并且具有使用恒定堆栈空间的额外好处。Creation. Recursion is a neat and elegant way to solve some problems, but it isn't perfect for every problem. I suspect that a purely recursive solution to creating the nodes would be much more complicated (i.e. less elegant) than Kornel Kisielewicz's straightforward iterative solution. That's because the
Tile
constructor needs to know the layout of all tiles in its immediate vicinity, in order to avoid recreating nodes that are already there.Traversal. The main problem in your node-traversal code is similar in that you will wind up with an infinite loop and blow the stack because every node will eventually "traverse" back to its parent, beginning the cycle again. I presume you're trying to visit every tile exactly once, right? In that case
TraverseGrid()
needs to have a parameter telling it which direction we are entering the node from, so that we avoid traversing back that way.But that's not enough -- you also need more discipline in deciding which directions to go. Simply spreading out in all directions except the direction we entered from will still wind up in an infinite loop and stack overflow, since any three adjacent tiles will cycle endlessly. In order to do this recursively you need to really think about which strategies will wind up visiting each node once and only once.
One possibility would be changing the signature of
TraverseGrid()
toTraverseGrid(Tile *inputTile, int fromDir, bool leftmost)
and then using the following rules:leftmost = false
.leftmost = false
.leftmost
, and there is a node to our lower left, then also traverse to that node, passingleftmost = true
.Of course
fromDir
andleftmost
could be combined into a single parameter, but this gives the general idea.Another alternative would be keeping a
visited
flag in each tile which is checked before traversing to that tile. Then your traversal will be a flood fill. But again, a simple iterative traversal is likely to be much simpler and easier to understand, and has the additional benefit of using constant stack space.在类声明中,有第二个构造函数
Tile(char *Filename);
。也许这个构造函数用于创建主节点,但没有正确初始化neighbor
? (它的实现没有显示。)在不相关的节点上,构造函数中有一个重复的
strcpy()
,它没有任何用途,可能只会导致问题:In the class declaration there is a second constructor
Tile(char *Filename);
. Maybe this constructor is used to create the main node, but doesn't initializeneighbor
properly? (Its implementation isn't shown.)And on an unrelated node, you have a duplicate
strcpy()
in the constructor that doesn't serves any purpose and might only lead to problems:我实际上做了同样的事情,但我的模式更像是这样的:
这使得很容易找出可以达到的目标,但会强制出现奇怪的偏移模式。我刚刚摆脱了(在上面的示例中)00,01,10 和 20,使其更像是这样的十六进制模式:
因此,如果您查看上面的模式,可达性始终相同:
从 23 (调用 2 " a" 和 3 "b") 你可以得到:
这个模式对于整个网格来说应该是正确的。
编辑:
我本来打算在评论中这样做,但它太长了。我可以看到两种方法。
1)分配节点数组(null,不分配它们)。每当您需要分配一个节点时,就这样做,但是每当您需要引用一个节点时,请使用数组地址,如果它为空,则填充它,如果它有值,则使用该值。巨大的空数组不应该占用那么多内存,但如果它们占用了...
2) 创建一个 HashSet 来保存节点,其中节点类的哈希值计算如下:(a << 32 || b).通过这种方式,您可以立即查找先前的节点是否存在。记住也要重载“equals”(只有当比较对象是相同类型并且 a 和 b 相等时,它才应该返回 true)。
在已知边界的人口最多的系统中,1 将节省内存,但如果您的系统稀疏(如您所说),则 #2 可以节省大量内存,而不会降低效率。
I actually did the same thing but my pattern was more like this:
This makes it pretty easy to figure out what can be reached, but forces a strange offset pattern. I just got rid of (in the above example) 00,01,10 and 20 to make it more of a hex pattern like this:
So if you look at the pattern above, reachable is always the same:
from 23 (call 2 "a" and 3 "b") you can get to:
This pattern should hold correct for the entire grid.
EDIT:
I was going to do this in a comment but it got too long. I can see two approaches.
1) Allocate the array of nodes (null, don't allocate them). Whenever you need to allocate a node, just do so, but use the array address whenever you need to reference a node, if it's null populate it, if it has a value use that value. Huge empty arrays shouldn't take up that much memory, but if they do...
2) Create a HashSet to hold your nodes where the Hash value of the node class is calculated like this: (a << 32 || b). In this way you can instantly look up to see if a previous node existed or not. Remember to overload "equals" as well (it should return true only if the compared object is the same type and a and b are equal).
In a mostly populated system where bounds are known, 1 will save memory, but if your system is sparse (as you claim) then #2 could save a LOT of memory with no cost to efficiency.