以下递归代码的时间复杂度是多少?

What is the time complexity of the following recursive code

本文关键字:多少 时间复杂度 递归 代码      更新时间:2023-10-16

不确定它是否相关,但我试图解决的问题是

~~~~~~~~~~~~~~~与所有建筑物的距离最短~~

~~~~~~~~~~~~~~~你想在一块空地上建造一所房子,在最短的距离内到达所有建筑物。您只能向上、向下、向左和向右移动。您将获得一个值为 0、1 或 2 的 2D 网格,其中:

每个0都标记着一块空地,您可以自由通过。 每个 1 标记一个您无法通过的建筑物。 每个 2 标记一个您无法通过的障碍。

找到与空地的最小距离,可以进入所有建筑物 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

尽管我的解决方案在小输入上正常工作,但由于时间复杂度大,较大的输入超时。

但是,我不确定此解决方案的确切时间复杂度,并希望正确了解此解决方案的时间复杂度,以便我可以在可能的情况下尝试改进它。

我很确定外循环的时间复杂度是 O(MN((M 是行的总数,N 是总行数(,因为我们在网格的所有位置上循环,但我不确定的是递归 shortDist 方法的时间复杂度。它是否也是 O(MN(,因为在最坏的情况下它会触及网格的每个位置,因此该解决方案的总时间复杂度将是 O(M^2 * N^2( 还是其他什么,如果是这样,如果有人可以解释一下我会很棒。

我的解决方案是

class Solution {
public:
int shortestDistance(vector<vector<int>>& grid) {
vector<std::pair<int,int>> dir = {{-1,0}, {1,0}, {0,-1}, {0,1}};
// pair(row, col) -> distance
map<std::pair<int,int>, int> cache;
vector<vector<bool>> visited(grid.size(), vector<bool>(grid[0].size(), false)); // to check if we have already visited that location on the grid
int minDist = INT_MAX;
int maxCount =0;
// Finding number of 1's
for(int i =0; i< grid.size(); i++)
{
for(int y =0; y < grid[0].size(); y++)
{
if(grid[i][y] == 1)
{
maxCount++;
}
}
}
// For each 0 find the distance of each 1's and their count
// if the count of 1's is less than the number of 1's in the
// grid that grid position is not an acceptable pos
for(int i =0; i< grid.size(); i++)
{
for(int y =0; y < grid[0].size(); y++)
{
if(grid[i][y] == 0)
{
shortDist(grid, cache, dir, visited, i, y, 0);
int dist =0;
int count = cache.size();// number of 1's
if(count == maxCount)
{
// if we are here it implies that the empty land space have path to all the buildings
for(auto iter = cache.begin(); iter != cache.end(); iter++)
{
dist += iter->second;
}
if(dist < minDist)
{
minDist = dist;
}
}
cache.clear();
}       
}
}
return minDist == INT_MAX ? -1 : minDist;
}
void shortDist(vector<vector<int>>& grid, map<std::pair<int,int>, int>& cache, vector<std::pair<int,int>>& dir, vector<vector<bool>>& visited, int row, int col, int dist)
{
if(row < 0 || col < 0 || row >= grid.size() || col >= grid[0].size())
{
return;
}
if(grid[row][col] == 2)
{
return;
}
if(grid[row][col] == 1)
{
auto found = cache.find(make_pair(row, col));
if(found == cache.end())
{
cache[(make_pair(row, col))] = dist;
}
else
{
found->second = min(found->second, dist);
}
return;
}
if(visited[row][col])
{
return;
}
visited[row][col] = true;
for(int i =0; i < dir.size(); i++)
{
int newRow = row + dir[i].first;
int newCol = col + dir[i].second;
shortDist(grid, cache, dir, visited, newRow, newCol, dist+1);
}
visited[row][col] = false;
}
};

据我所知,shortDist是主要贡献者。

shortDist具有的函数用于findO(log(MN((,因为缓存最多可以包含行 * cols 条目,(使用std::map,如果您有一个完美的哈希函数,则使用std::unordered_map只有 O(1(。然后你递归距离,即 D = max(M,N(,实际上你访问每个 MN 点。对于来自shortestDistance的每个呼叫的总计 O(MN log(MN((。

shortestDistance在网格上的第二个循环中,前两个循环有 O(MN(,然后是 O(MN( 循环缓存,给出 O(M^2*N^2(,对 shortDist 的调用是 O(M^2N^2 log(MN((。

如果使用另一个 MN 数组直接查找值,则可以保存日志 (MN(。

实施优化。

你对 shortDist 的调用有太多的参数。

dir向量应该是constexpr std::array,因为它永远不会改变,并且在所有搜索中使用。

Cachevisited应该是类的成员,即 shortestDistance 为每个调用重置,如果不是,则 Solution 的实例仅使用一次。

考虑到您这样做的次数,将网格作为参数与您一起拖动似乎也很浪费。使其成为类引用或副本将解决此问题。

那么shortDist只有合理的 3 个参数。

您可以通过使网格一维并自行计算索引来节省大量性能损失,从而将每次 x,y 查找从 2 个内存访问减少到 1 个内存访问shortDist