以下最优解为 O( n ) 的运行时间,不仅计算是否存在两个以上的连续序列,而且计算
您的问题的解决方案可以非常有效且简单地解决,无需使用任何类型的图论,而只需使用您对数组的了解,并利用您对它们在内存中的布局方式的了解。下面的 C 程序演示了这个原理。
static inline void TrackLength(int **hpnCountPtr, int *pSeqStart)
{
(*((*hpnCountPtr) ? (*hpnCountPtr) : (*hpnCountPtr = pSeqStart)))++;
}
static int FindSequences(char *pcbGrid, int nWidth, int nHeight)
{
int nColIndex, nRowIndex, nSeqTotal;
int *panResult, *pnHorzSeq, *panVTable;
int **panVertSeqs;
size_t nResultLen;
char *pcbCheckPtr;
nResultLen = (size_t) (2 * nWidth * nHeight) * sizeof(int);
panResult = (int*) memset(alloca(nResultLen), 0, nResultLen);
panVTable = panResult + (nWidth * nHeight);
panVertSeqs = (int**) alloca(nWidth * sizeof(int*));
for (nColIndex = 0; nColIndex < nWidth; panVertSeqs[nColIndex++] = (int*) NULL);
for (pcbCheckPtr = pcbGrid, nRowIndex = 0; nRowIndex < nHeight; nRowIndex++)
for (pnHorzSeq = (int*) NULL, nColIndex = 0; nColIndex < nWidth; nColIndex++, pcbCheckPtr++)
{
if (*pcbCheckPtr != 'X')
{
pnHorzSeq = panVertSeqs[nColIndex] = (int*) NULL;
continue;
}
TrackLength(&pnHorzSeq, panResult + (pcbCheckPtr - pcbGrid));
TrackLength(panVertSeqs + nColIndex, panVTable + (pcbCheckPtr - pcbGrid));
}
/* Insert Debug Information Here */
for (nSeqTotal = 0, nColIndex = nWidth * nHeight; nColIndex; nColIndex--)
if (*panResult++ > 1)
nSeqTotal++;
return nSeqTotal;
}
关键在于您的要求是只需要找到水平和垂直序列,而不是对角线。作为一个 2D 数组,这意味着可以通过知道 2D 数组是简单的平坦、连续的内存块来进行巨大的优化,可以一次从头到尾遍历。
利用内存安排,可以仅使用单个计数器跟踪水平序列,该计数器针对每个连续的内存位置递增X
,并且每次X
遇到非时重置为零。如果在阵列的 2D 视图中每个“行”的末尾强制重置计数器,这将成功跟踪水平序列。
在跟踪水平序列的同时,我们可以同时跟踪垂直序列。但是,由于数组中列中的相邻单元格以width
字节间距出现,因此我们必须保留一个计数器数组,width
这些计数器在我们扫描行时跟踪行中所有可能的垂直序列,每列一个序列。
最后,我的解决方案的真正美妙之处在于计数器实际上是指向两个 2D 数组的指针(一个用于跟踪垂直序列,一个用于跟踪水平序列)。每个数组中的每个位置都代表一个序列的潜在开始,当函数完成时,每个数组将在每个位置包含从该位置开始的每个序列的长度。
下图最好地说明了这一点:
Grid Horizontal Vertical
Sequences Sequences
....X...X. 0000100010 0000300030
.X..X...X. 0100100010 0300000000
.XXXX..XXX 0400000300 0011000201
.X.....X.. 0100000100 0000000000
水平序列数组中的每个数字代表从该点开始的水平序列的长度。垂直序列的垂直序列数组也是如此。
由于程序使一个并且恰好一个传递输入数组,并且每个数组元素的处理时间是恒定的,因此该算法在 O( n ) 中运行并且对于任何大小的网格都是最佳的。
如果在下面的程序中插入上述函数:
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <alloca.h>
#define GRID_WIDTH 18
#define GRID_HEIGHT 9
static char *s_szGrid =
"X X X X X X X X X "
" X X X X X X X X X"
"X X X X X X X X X "
" X X X X X X X X X"
"XXX X X X X X X X "
" X X X X X X X X X"
"XXXXX X X X X X X "
" X X X X X X X X X"
"X X XXX X X X X X ";
void main(void)
{
int nSeqTotal = FindSequences(s_szGrid, GRID_WIDTH, GRID_HEIGHT);
printf("\nGrid has %i contiguous sequences\n", nSeqTotal);
if (nSeqTotal)
printf(">>> %sone sequence detected\n", (nSeqTotal > 1) ? "more than " : "");
}
然后将以下调试代码块插入到您看到/* Insert Debug Information Here */
注释的函数中:
{
int nXIndex, nYIndex;
int *pnValue;
printf("Horizontal:\n");
for (pnValue = panResult, nYIndex = 0; nYIndex < nHeight; nYIndex++, printf("\n"))
for (nXIndex = 0; nXIndex < nWidth; nXIndex++, printf("%2i", *pnValue++));
printf("\nVertical:\n");
for (nYIndex = 0; nYIndex < nHeight; nYIndex++, printf("\n"))
for (nXIndex = 0; nXIndex < nWidth; nXIndex++, printf("%2i", *pnValue++));
}
然后程序将产生以下输出:
Horizontal:
1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0
0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1
1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0
0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1
3 0 0 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0
0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1
5 0 0 0 0 0 1 0 1 0 1 0 1 0 1 0 1 0
0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1
1 0 1 0 3 0 0 0 1 0 1 0 1 0 1 0 1 0
Vertical:
1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0
0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1
1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0
0 5 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1
1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0
0 0 0 3 0 1 0 1 0 1 0 1 0 1 0 1 0 1
1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0
0 0 0 0 0 2 0 1 0 1 0 1 0 1 0 1 0 1
1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0
Grid has 6 contiguous sequences
>>> more than one sequence detected
但是,如果输入网格更改为此:
static char *s_szGrid =
"X X X X X X X X X "
" X X X X X X X X X"
"X X X X X X X X X "
" X X X X X X X X X"
"X X X X X X X X X "
" X X X X X X X X X"
"X X X X X X X X X "
" X X X X X X X X "
"X X X X X X X X XX";
然后输出将是:
Horizontal:
1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0
0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1
1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0
0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1
1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0
0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1
1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0
0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 0
1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 2 0
Vertical:
1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0
0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1
1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0
0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1
1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0
0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1
1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0
0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 0
1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 1
Grid has 1 contiguous sequences
>>> one sequence detected