Блог пользователя Easy_

Автор Easy_, 10 лет назад, По-русски

Подскажите пожалуйста пример реализации dfs без рекурсии. Вот попробовал реализовать через стэк, но как-то криво работает..

int G[N][N]; // Матрица смежностей
...
void dfs(int p)
{
	stack<int> S;
	vector<int> v(N);
	int t;
	S.push(p);
	v[p]++;
	cout << p  + 1 << " ";
	while(!S.empty())
	{
		t = S.top();
		S.pop();
		for(int i = N - 1; i > 0; i--)
			if(G[t][i] && !v[i])
			{
				S.push(i);
				v[i]++;
				cout << i + 1 << " ";
			}
	}
}
  • Проголосовать: нравится
  • +8
  • Проголосовать: не нравится

»
10 лет назад, # |
  Проголосовать: нравится -12 Проголосовать: не нравится

Кстати, зачем вообще отдельно использовать стек когда есть вектор? Оо

  • »
    »
    10 лет назад, # ^ |
      Проголосовать: нравится +14 Проголосовать: не нравится

    Для читабельности кода. Вот читает кто-то твой код и видит слово вектор. И сразу думает, что ты тут какой-то линейной алгеброй занимаешься. А напишешь стек и всем ясно, что тут ДФС'ом попахивает.

  • »
    »
    10 лет назад, # ^ |
    Rev. 2   Проголосовать: нравится -7 Проголосовать: не нравится

    Было дело, писал какую-то программу. Там нужен был стек и памяти выделено очень мало... Ну так вот, программа со стеком забрала 60000KB, а тоже самое с вектором — около 3000KB. Был очень удивлен. До сих пор не понимаю, чем это вызвано. С того момента больше не использую стек...

    • »
      »
      »
      10 лет назад, # ^ |
        Проголосовать: нравится +1 Проголосовать: не нравится

      Вроде, std::stack по-умолчанию использует как хранилище не std::vector, а std::deque.

      • »
        »
        »
        »
        10 лет назад, # ^ |
          Проголосовать: нравится 0 Проголосовать: не нравится

        А не зависит ли это часом от конкретной реализации?

»
10 лет назад, # |
  Проголосовать: нравится 0 Проголосовать: не нравится

cout << i + 1 << " ";

Это не будет работать, т.к. мы не переключаемся на выводимую вершину сразу. Чтобы это работало, нужно выводить в теле while-цикла при первом заходе в неё.

  • »
    »
    10 лет назад, # ^ |
      Проголосовать: нравится 0 Проголосовать: не нравится

    Спасибо конечно, а как это влияет? Не вижу разницы каким образом первую вершину выводить, с которой начинается обход..

    • »
      »
      »
      10 лет назад, # ^ |
        Проголосовать: нравится +5 Проголосовать: не нравится

      Тут дело не в первой вершине, а таки в каждой. Ну, если ты хочешь получить вершины в порядке их обхода, надо и выводить их в порядке обхода, то есть при входе в вершину или при выходе из неё. А ты выводишь при добавлении в стек. С бфс-ом прокатило бы, но с нерекурсивным дфс не прокатит, так как ты переходишь к вершине не сразу после её добавления в стек, а когда добавишь в него всех потомков предыдущей вершины.

      • »
        »
        »
        »
        10 лет назад, # ^ |
          Проголосовать: нравится 0 Проголосовать: не нравится

        Спасибо большое, ошибку понял:

        void dfs(int p)
        {
        	stack<int> S;
        	vector<int> v(N);
        	int t;
        	S.push(p);
        	v[p]++;
        	while(!S.empty())
        	{
        		t = S.top();
        		S.pop();
        		cout << t + 1 << " ";
        		for(int i = N - 1; i >= 0; i--)
        			if(G[t][i] && !v[i])
        			{
        				S.push(i);
        				v[i]++;
        			}
        	}
        }
        
        • »
          »
          »
          »
          »
          10 лет назад, # ^ |
            Проголосовать: нравится +1 Проголосовать: не нравится

          и что, сейчас работает? Больше похоже на бфс с перебором ребер в обратном порядке.

          • »
            »
            »
            »
            »
            »
            10 лет назад, # ^ |
              Проголосовать: нравится 0 Проголосовать: не нравится

            Это был бы бфс, если бы контейнером являлся queue...

            • »
              »
              »
              »
              »
              »
              »
              10 лет назад, # ^ |
                Проголосовать: нравится +7 Проголосовать: не нравится

              Согласен, но заменить queue на stack не достаточно, чтобы из бфса сделать дфс.

              • »
                »
                »
                »
                »
                »
                »
                »
                10 лет назад, # ^ |
                  Проголосовать: нравится -7 Проголосовать: не нравится

                Да ладно?. Вроде как достаточно.

                • »
                  »
                  »
                  »
                  »
                  »
                  »
                  »
                  »
                  10 лет назад, # ^ |
                    Проголосовать: нравится +2 Проголосовать: не нравится

                  С чего бы? Внизу у фиолетового норм решение, а это неправильное.

                • »
                  »
                  »
                  »
                  »
                  »
                  »
                  »
                  »
                  10 лет назад, # ^ |
                    Проголосовать: нравится +5 Проголосовать: не нравится

                  Для дерева было бы достаточно.

                  Если в графе есть циклы, то это уже не совсем dfs. Если в определенную вершину есть ребро как с вершины х, так и из какого-то из ее потомков, то есть вероятность того, что по ребру из потомка она была бы посещена раньше, чем по ребру из предка, которое мы ей "забронировали".

                  Пример — граф вида

                  1 2
                  1 3
                  1 4
                  1 5
                  ...
                  1 100
                  2 100
                  

                  Мы обойдем его как 1 2 3... 100, хотя в настоящем dfs при рассмотрении вершины 2 вершина 100 еще не посещена, и порядок должен быть 1 2 100 3 4 5...

                • »
                  »
                  »
                  »
                  »
                  »
                  »
                  »
                  »
                  10 лет назад, # ^ |
                    Проголосовать: нравится 0 Проголосовать: не нравится

                  =================================

                  Спасибо за подробное объяснение. Сам всегда думал, что простой смены очереди на стек достаточно.

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

»
10 лет назад, # |
  Проголосовать: нравится -24 Проголосовать: не нравится

И нахрена же писать дфс без рекурсии?

  • »
    »
    10 лет назад, # ^ |
      Проголосовать: нравится 0 Проголосовать: не нравится

    Ну вдруг памяти на рекурсию жалко. Да и просто ради интереса/общего развития можно написать.

  • »
    »
    10 лет назад, # ^ |
      Проголосовать: нравится +5 Проголосовать: не нравится

    во-первых, чтобы не было переполнения стека

    во-вторых, вызов функций во многих языках очень дорог

»
10 лет назад, # |
  Проголосовать: нравится +5 Проголосовать: не нравится

Например вот так: http://pastebin.com/t8egDMrs

Вроде бы порядок обхода как в настоящем dfs.