我研究过使用 Dijkstra(如 Weather Vane 所建议的那样),这将要求存储每个网格单元到起点的距离和前一个单元的方向。
不幸的是,32x16 网格上的路径距离可能大于 255;我找到的最长路径距离为 319(见下图,左)。这意味着距离不适合 8 位,并且距离矩阵的大小为 1024 字节。
左:最长路径(距离=319)。右图:最大等距单元数(距离 16 处有 72 个单元)
但是,在所有距离都等于 1 的方形网格中,您可以将 Dijkstra 简化为不使用距离矩阵的广度优先搜索;如果您使用先进先出队列,则按照与起始单元的距离顺序访问单元,因此您无法找到到已访问单元的更短路径。
fifo 队列将包含一定距离的每个单元格,然后逐渐过渡到距离 + 1,依此类推。队列的最大大小取决于可以有多少等距单元;我发现的最大值是 72(见上图,右图),在从前一个距离过渡期间,这需要一个可以容纳 76 个单元格或 152 个字节的坐标的队列。
算法返回的路径是一个数组,最多保存 320 个单元格的坐标,因此它的最大大小为 640 字节。在构造这个数组之前,队列可以被丢弃,所以只有方向网格和路径同时在内存中。
下面是简化算法的代码示例,只有一个方向矩阵和一个fifo队列;它可能在很多方面都可以改进,但它证明了这个想法。 findPath() 函数使用最少 664 到最多 1152 字节的已分配内存(取决于路径长度)加上大约 20 字节用于附加变量。
这可以进一步减少,例如通过将方向矩阵存储为 4 位半字节,将其大小从 512 字节减少到 256 字节(但需要更多计算),或者通过将路径作为上/右/下/左方向序列而不是单元坐标返回,这将每步只需要 2 位,将其最大大小从 640 字节减少到 80 字节。
#include <stdlib.h> // gcc -std=c99
short int findPath(char grid[][32], char x1, char y1, char x2, char y2, char **path) {
char (*dir)[16][32] = calloc(512, 1); // allocate direction matrix: 512 bytes (zeros)
(*dir)[y2][x2] = 5; // mark starting cell as visited (search backwards)
char *queue = malloc(152); // allocate fifo queue: 152 bytes
queue[0] = x2; queue[1] = y2; // put starting cell in queue (search backwards)
unsigned char qRead = 0, qWrite = 2; // queue pointers
char qCurSize = 1, qNextSize = 0; // queue size per distance
short int distance = 0; // distance to current cell
char dx[4] = {0, 1, 0, -1}; // up, right, down, left
while (qRead != qWrite && !(*dir)[y1][x1]) { // until queue empty (fail) or target reached
char x = queue[qRead++], y = queue[qRead++]; // take oldest cell from queue
qRead %= 152; // wrap-around queue pointer
for (char i = 0; i < 4; i++) { // check 4 neighbouring cells
char nx = x + dx[i], ny = y + dx[3 - i]; // coordinates of neighbouring cell
if (nx >= 0 && nx < 32 && ny >= 0 && ny < 16 // coordinates not off-grid
&& !grid[ny][nx] && !(*dir)[ny][nx]) { // traversable unvisited cell
(*dir)[ny][nx] = i + 1; // store direction 1-4
queue[qWrite++] = nx; queue[qWrite++] = ny; // put cell in queue
qWrite %= 152; // wrap-around queue pointer
++qNextSize; // increment queue size for next distance
}
}
if (!--qCurSize || (*dir)[y1][x1]) { // current distance done or target reached
qCurSize = qNextSize; // switch to distance + 1
qNextSize = 0;
++distance;
}
}
free(queue); // free up queue memory for path
if (!(*dir)[y1][x1]) distance = -1; // no path found
else { // path found
*path = malloc(distance * 2 + 2); // allocate path array: 2 bytes per step
(*path)[0] = x1; (*path)[1] = y1; // starting position (forward)
for (short int i = 1; i <= distance; i++) { // retrace steps
char d = (*dir)[y1][x1] - 1; // direction of previous step 0-3
x1 -= dx[d]; y1 -= dx[3 - d]; // go back to previous position
(*path)[i * 2] = x1; (*path)[i * 2 + 1] = y1; // add cell to path
}
}
free(*dir); // discard direction matrix
return distance + 1; // return number of cells in path
}
int main() {
char grid[][32] = // max queue size: 76
{{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}};
char x1 = 31, y1 = 0, x2 = 16, y2 = 7, *path = NULL;
short int steps = findPath(grid, x1, y1, x2, y2, &path);
// do stuff
free(path); // discard path array
return 0;
}