简体   繁体   English

如何避免此代码中的堆栈溢出(递归函数)

[英]How to avoid stack overflow in this code, (Recursive function)

I was trying to solve a programming contest problem. 我正在尝试解决编程竞赛问题。 I am pretty much a noob in this department (I think I have a lot to learn). 我在这个部门几乎是个菜鸟(我想我有很多东西要学)。 I tried to solve the question, which included reading a 2D array( nxm ) and finding out the blobs in it. 我试图解决这个问题,其中包括读取2D数组( nxm )并找出其中的斑点。 Blobs are formed by contiguous lit pixels(denoted by # ). 斑点由连续的亮像素(由#表示)形成。 Unlit pixels (denoted by . ). 未点亮的像素(以.表示)。 I tried to find a blob by using a recursive method Blob::form() . 我试图通过使用递归方法Blob::form()找到一个blob。 Sample input might look like this 示例输入可能看起来像这样

1
6 6
#...#.
.#.#.#
##..#.
......
.#.#.#
#...#.

I came up with the solution in a rush. 我匆忙想出了解决方案。 And it's not much. 而且不多。 But as always it fails in the worst condition n = m = 1000 and all chars are # . 但是像往常一样,它在最坏的情况下失败n = m = 1000并且所有字符都是# A 1000 x 1000 version of this: 此版本的1000 x 1000:

1
3 3
###
###
###

The problem I assume is stack overflow. 我认为的问题是堆栈溢出。 I have found out that the program is crashing while forming the blob. 我发现程序在形成Blob时崩溃。

#include <iostream>
#include <string>
#include <sstream>
#include <vector>
#include <algorithm>

using namespace std;
int pat[1000][1000],n,m;
char a[1000][1000];

struct point
{
    int x,y;
};

bool inBounds(point p)
{
    if(p.x < n && p.x >=0 && p.y < m && p.y >= 0) return true;
    else return false;
}
bool isAblob(int i,int j)
{
    point p[8];
    p[0].x = i-1; p[0].y =  j;
    p[1].x = i+1; p[1].y =  j;
    p[2].x = i+1; p[2].y =  j+1;
    p[3].x = i-1; p[3].y =  j-1;
    p[4].x = i-1; p[4].y =  j+1;
    p[5].x = i+1; p[5].y =  j-1;
    p[6].x = i; p[6].y =  j-1;
    p[7].x = i; p[7].y =  j+1;

    for(int k=0;k<8;k++)
    {
        if(inBounds(p[k]))
        {
            if(a[p[k].x][p[k].y] == '#') return true;
        }
    }
    return false;
}


class Blob
{
public:
    long long int pow;

    Blob(int i, int j)
    {
        this->pow = 0;
        point po;
        po.x=i;
        po.y=j;
        this->form(&po);
    }

    int getPow()
    {
        return this->pow;
    }

    void form ( point *p)
    {
        if(inBounds(*p))
        {
            if(a[p->x][p->y] == '#' && !pat[p->x][p->y])
            {
                a[p->x][p->y] = 1;
                this->pow++;
                point *e = new point;
                e->x = p->x-1; e->y =  p->y;if(pat[e->x][e->y] == 0)form(e);
                e->x = p->x+1; e->y =  p->y;if(pat[e->x][e->y] == 0)form(e);
                e->x = p->x+1; e->y =  p->y+1;if(pat[e->x][e->y] == 0)form(e);
                e->x = p->x-1; e->y =  p->y-1;if(pat[e->x][e->y] == 0)form(e);
                e->x = p->x-1; e->y =  p->y+1;if(pat[e->x][e->y] == 0)form(e);
                e->x = p->x+1; e->y =  p->y-1;if(pat[e->x][e->y] == 0)form(e);
                e->x = p->x; e->y =  p->y-1;if(pat[e->x][e->y] == 0)form(e);
                e->x = p->x; e->y =  p->y+1;
                if(pat[e->x][e->y] == 0)form(e);
            }
        }
        return;
    }
};

int main()
{
    int t;

    cin >> t;
    for (int q = 0; q < t; q++)
    {
        cin >> n >> m;
        int bnum = 0;
        Blob *b[(n*m)/2];
        vector <int> pows;
        cin.get();
        for(int i=0;i<n;i++)
        {
            for(int j = 0; j<m;j++)
            {
                a[i][j] = cin.get();
                pat[i][j] = 0;
            }
            cin.get();
        }


        for(int i=0;i<n;i++)
        {
            for(int j = 0; j<m;j++)
            {
                if(a[i][j] == '#' && pat[i][j] == 0)
                {
                    if(isAblob(i,j))
                    {
                        bnum++;
                        b[bnum] = new Blob(i,j);
                        pows.push_back(b[bnum]->getPow());
                    }
                    else continue;
                }
                else continue;
            }
        }
        sort(pows.begin(),pows.end());
        cout << endl << bnum;

        for(int i=1;i<=bnum;i++)
        {
            if(i==1) cout << endl;
            if(i!=1) cout << " ";
            cout << pows[i-1];
        }
    }
}

I am sure my code is buggy and inefficient. 我确信我的代码有错误并且效率低下。 I am wondering if someone can give me an insight on how to avoid these problems in the future. 我想知道是否有人可以让我深入了解将来如何避免这些问题。 Better implementations can be helpful too. 更好的实现方式也可能会有所帮助。 But what I am looking for are tips for avoiding stack overflows in the future. 但是我正在寻找的是避免将来发生堆栈溢出的提示。

It looks to me like the integer matrix pat[][] is initialized to all zeros, tested in several places, but never set to anything else. 在我看来,整数矩阵pat[][]已初始化为全零,在多个位置进行了测试,但从未设置为其他任何值。 So the Blob constructor calls form() which calls itself almost unconditionally, until there is a crash. 因此,Blob构造函数调用form() ,它几乎无条件地调用自身,直到发生崩溃为止。 I say "almost" because there are other conditions leading up to the recursive call, but the last check (a value in pat ) always succeeds. 我说“几乎”是因为还有其他条件导致递归调用,但是最后一个检查( pat的值)始终会成功。

I could have read too fast, and if so, I will take my beating humbly. 我可能读得太快了,如果是的话,我会谦卑地跳动。 ;-) ;-)

An easy way to avoid recursion while not changing the logic of the program is to use a stack data-structure directly, instead of through the call-stack. 在不更改程序逻辑的情况下避免递归的一种简单方法是直接使用堆栈数据结构,而不是通过调用堆栈。

Here's a modified version of your Blob class that uses std::stack in your form function: 这是Blob类的修改后的版本,它在表单函数中使用std :: stack

class Blob
{
public:
    long long int pow;

    Blob(int i, int j)
    {
        this->pow = 0;
        point po;
        po.x=i;
        po.y=j;
        this->form(po);
    }

    int getPow()
    {
        return this->pow;
    }

    void form (point p)
    {
        std::stack<point> s;
        s.push(p);

        while (!s.empty())
        {
            p=s.top();
            s.pop();

            if (!inBounds(p))
                continue;

            if(a[p.x][p.y] == '#' && !pat[p.x][p.y])
            {
                a[p.x][p.y] = 1;
                this->pow++;
                point e;
                e.x = p.x-1;    e.y =  p.y;     if(pat[e.x][e.y] == 0)s.push(e);
                e.x = p.x+1;    e.y =  p.y;     if(pat[e.x][e.y] == 0)s.push(e);
                e.x = p.x+1;    e.y =  p.y+1;   if(pat[e.x][e.y] == 0)s.push(e);
                e.x = p.x-1;    e.y =  p.y-1;   if(pat[e.x][e.y] == 0)s.push(e);
                e.x = p.x-1;    e.y =  p.y+1;   if(pat[e.x][e.y] == 0)s.push(e);
                e.x = p.x+1;    e.y =  p.y-1;   if(pat[e.x][e.y] == 0)s.push(e);
                e.x = p.x;      e.y =  p.y-1;   if(pat[e.x][e.y] == 0)s.push(e);
                e.x = p.x;      e.y =  p.y+1;   if(pat[e.x][e.y] == 0)s.push(e);
            }

        }
    }
};

Note that this also fixes your memory leak. 请注意,这也可以修复内存泄漏。

In general, the problem you are trying to solve seems to be finding "connected components" with a square-shaped neighborhood. 通常,您要解决的问题似乎是找到具有正方形邻域的“连接的组件”。 Usually, you'd use a disjoint-set data-structure to solve this, which does not need a stack or recursion.With that, you can just scan thru the field once and you have the sizes of all your connected components, not just the one where you check for a blob. 通常,您可以使用不相交的数据结构来解决此问题,不需要堆栈或递归,这样一来,您只需扫描一次字段即可获得所有连接组件的大小,而不仅仅是检查斑点的那个。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM