Как мне реализовать функцию заливки в моей программе на С ++? - PullRequest
0 голосов
/ 11 ноября 2018

Я сейчас собираюсь включить что-то вроде этого:

.............
.............
..XXX.....X..
..XXX.....X..
..XXX........
..XXX........
..XXXXXXX....
..XXXXXXX....
..XXXXXXX....
.............
.............

в это:

.............
.............
..XXX.....O..
..XXX.....O..
..XXX........
..XXX........
..XXXXXXX....
..XXXXXXX....
..XXXXXXX....
.............
.............

при вводе пользователем ./a.exe input4.txt floodfill 2 10 o

Я полагаю, что мне потребуется реализовать некоторую рекурсию в программе, чтобы иметь возможность просматривать только те индексы, которые соответствуют указателю пользователя (включая позиции вверх, вниз, влево и вправо) вместо чтения всего вектора (что я не возражаю, но не понимаю, как бы я начал это делать).

вот код, который у меня есть для функции заливки:

void floodfilll(vector<vector<char>> &vec, int x, int y, char r, char dum)
{
 int ii; 
 ii = 0;
 int jj; 
 jj = 0;
for (int i = 0; i < vec.size(); i ++) {
        for (int j = 0; j < vec[i].size(); j++) {
            if (vec[i][j] == r) {
                vec[i][j] == r;
                if ((i + ii) > 0) {
                    if (vec[i-1][j] == r)
                        vec[i-1][j] = dum;
                        vec[i][j] == r;
                    ii--;
                    floodfilll(vec, x + ii, y, r, dum);
                }
                if ((j + jj) > 0) {
                    if(vec[i][j-1] != r)
                        vec[i][j-1] = dum;
                        vec[i][j] == r;
                    jj--;
                    floodfilll(vec, x, y + jj, r, dum);
                }
                if ((i + ii)<vec.size()) {
                    if (vec[i+1][j] != r)
                        vec[i+1][j] = dum;
                        vec[i][j] == r;
                    ii++;
                    floodfilll(vec, x + ii, y, r, dum);
                }
                if ((j + jj)<vec[i].size()) {
                    if (vec[i][j+1] != r)
                        vec[i][j+1] = dum;
                        vec[i][j] == r;
                    jj++;
                    floodfilll(vec, x, y + jj, r, dum);
                }
            }
        }
        replacee(vec, dum, r);
    }
}

ПРИМЕЧАНИЕ. Я использую функцию под названием replacee для замены Var dum на Var R. Var dum назначается 'i', а r - 'X'.

Кроме того, текстовый файл анализируется как 2d вектор char (char ) **

Просто так устроена остальная часть моей программы. Вот функция замены:

    void replacee(vector<vector<char>> &vec, char oldd, char neww)
    {
        for (vector<char> &v : vec) // reference to innver vector
        {
            replace(v.begin(), v.end(), oldd, neww); // standard library algorithm
        }
    }

Это основной файл, который я использую:

int main(int argc, char* argv[]) {

fstream fin; char ch;
string name (argv[1]); //File Name.
vector<vector<char>> data;
// 2D Vector.
vector<char> temp;
// Temporary vector to be pushed 
// into vec, since its a vector of vectors.
fin.open(name.c_str(),ios::in);
// Assume name as an arbitary file.
string argument2 (argv[2]);
while(fin)
{
    ch = fin.get();
    if(ch!='\n') {
        temp.push_back(ch);
    }
    else 
    { 
        data.push_back(temp); 
        temp.clear(); 
    }
}
if (argument2 == "floodfill") {
    string argument3 (argv[3]);
    string argument4 (argv[4]);
    string argument5 (argv[5]);
    int x = 0;
    int y = 0;
    stringstream xx(argument3);
    stringstream yy(argument4);
    xx >> x;
    yy >> y;
    floodfilll(data, x, y, argument5[0], 'i');
    for (int m = 0; m < data.size(); m ++) {
        for (int n = 0; n < data[m].size(); n++) {
            cout << data[m][n];
        }
        cout << endl;
    }
}

fin.close();
} 

Извините, если мне кажется, что я просто вставляю код для захвата, это в том случае, если у кого-то есть решение вне моего мышления. Функции int main и replacee работают как положено. Мне просто нужна помощь, чтобы найти способ заставить floodfilll работать правильно.

Это вывод, который я получаю с моим кодом:

$ ./a.exe input4.txt floodfill 2 10 o
.............
.............
..XXX.....X..
..XXX.....X..
..XXX........
..XXX........
..XXXXXXX....
..XXXXXXX....
..XXXXXXX....
.............

Ответы [ 2 ]

0 голосов
/ 11 ноября 2018

Один из вариантов - использовать рекурсию, как предлагает другой ответ. Однако лично я предпочитаю избегать рекурсии там, где это не нужно. Альтернативой является подход на основе очередей.

void floodfill (std::vector<std::vector<char>>& v, unsigned int x, unsigned int y, char r) {
    char init = v[x][y];
    if (init == r) return; //We don't want to get caught in an endless loop. 
    if (x >= v.size() || y >= v[x].size) return; //Index out of bounds. 
    std::queue<std::pair<unsigned int, unsigned int>> q;
    q.push(std::make_pair(x, y)); //Push the initial position into the queue.
    v[x][y] = r; //Replace the initial point. 
    while (!q.empty()) {//Keep looking at relevant neighbours so long as there is something in the queue. 
        auto pt = q.top(); //Collect the first entry.
        q.pop(); //Remove it, we don't want to keep processing the same point. 
        //Now add neighbours if they match our initial point. 
        if(pt.first > 0 && v[pt.first - 1][pt.second] == init)
            q.push(std::make_pair(pt.first - 1, pt.second);
            v[pt.first - 1][pt.second] = r; //Replace the value here to avoid pushing the same point twice. 
        if(pt.first + 1 < v.size() && v[pt.first + 1][pt.second] == init)
            q.push(std::make_pair(pt.first + 1, pt.second);
            v[pt.first + 1][pt.second] = r;
        if(pt.second > 0 && v[pt.first][pt.second - 1] == init)
            q.push(std::make_pair(pt.first, pt.second - 1);
            v[pt.first][pt.second - 1] = r;
        if(pt.second + 1 < v[pt.first].size() && v[pt.first][pt.second + 1] == init)
            q.push(std::make_pair(pt.first, pt.second + 1);
            v[pt.first][pt.second + 1] = r;
    }
}

Это дает вам BFS-подобный шаблон заливки без рекурсии. В качестве альтернативы вы можете также использовать stack вместо queue, тогда заливка будет вести себя больше как DFS (гораздо более похожая на то, что будет делать рекурсивный шаблон). Он может даже работать немного лучше, чем очередь, учитывая, что структура данных немного проще.

0 голосов
/ 11 ноября 2018

Почему вы проходите итерацию по всему полю в каждой рекурсии?

Обычно заливка работает следующим образом:

  1. У вас есть конкретная отправная точка.
  2. Вы заполняете эту начальную точку нужным цветом
  3. Вы проверяете для каждого из четырех (или 8, если вы учитываете и диагонали) соседей, имеют ли они тот же цвет, что и исходная точка; если это так, вы продолжаете рекурсивно.

Так что реализация может выглядеть так:

void floodfill
(
        std::vector<std::vector<char>>& v,
        unsigned int x, unsigned int y, char r
)
{
    char p = v[x][y];
    v[x][y] = r;
    if(x > 0 && v[x - 1][y] == p)
        floodfill(v, x - 1, y, r);
    if(x + 1 < v.size() && v[x + 1][y] == p)
        floodfill(v, x + 1, y, r);
    if(y > 0 && v[x][y - 1] == p)
        floodfill(v, x, y - 1, r);
    if(y + 1 < v[x].size() && v[x][y + 1] == p)
        floodfill(v, x, y + 1, r);
}

Обратите внимание, что я не проверял, чтобы цвет заливки совпадал с регистром начального пикселя, и при этом я изначально не проверял проверку диапазона x и y. Для эффективности я бы не добавил эти проверки в рекурсивную функцию, но в особую функцию ввода, запускающую рекурсию, поэтому они выполняются только один раз, когда это необходимо, и не повторяются без необходимости.

...