3

Это проблема 9.6 из Крекинг Кодирование интервью (5 й издание)Какова пространственная сложность этого алгоритма?

реализовать алгоритм, чтобы напечатать все допустимые комбинации п-пар скобок
Пример
Входной сигнал: 3
Выход: «(()),()()()()()(),() (()),()()()«

Вот алгоритм, который я реализовал (на Java)

private static Set<String> getAllComb(int n) { 
     Set<String> allPoss = new HashSet<String>(); 
     if(n>0) { 
      if(n==1) { 
       allPoss.add("()"); 
      } else { 
       Set<String> before = getAllComb(n-1); 
       for(String phrase: before) { 
        int length = phrase.length(); 
        for(int start = length - 2; start>=0; start--) { 
         if(phrase.charAt(start) == '(') { 
          String phraseToConsider = phrase.substring(0, start+1) + "()" + 
           phrase.substring(start + 1); 
          if(!allPoss.contains(phraseToConsider)){ 
           allPoss.add(phraseToConsider); 
          } 
         } 
        } 
        String phraseToConsider = "()" + phrase.substring(0); 
        if(!allPoss.contains(phraseToConsider)){ 
         allPoss.add(phraseToConsider); 
        } 
       } 
      } 
     } 
     return allPoss; 
} 

Это дает правильный выход. Я знаю, что интервьюеры (по крайней мере, в Amazon) любят задавать вам сложность времени и пространства вашего решения. Для временной сложности я смог показать, что алгоритм работает в O (n) с рекуррентным соотношением. У меня возникают проблемы с анализом сложности пространства. I это рекурсивное решение, поэтому должно быть не менее O (n) Но при каждом рекурсивном вызове я также генерирую множество, ограниченное n. Будет ли общее пространство O (n) из-за n рекурсивных вызовов или оно O (n) из-за установленного размера связанного n для каждого рекурсивного вызова n?

+0

Некоторые подсказки на стороне, которые не имеют прямого отношения к вашей проблеме: 1) Вы должны иметь возможность использовать N = 0 в качестве базового футляра; его общее в рекурсивных проблемах не нужно рассматривать N = 1 специально 2) Если вы используете буфер длиной 2n, который рекурсивные вызовы «заполняют», вы должны иметь возможность свести сложность пространства до O (n). – hugomg

+0

Для n = 4 существует 14 возможных способов записи правильно скопированных скобок. Буфер длиной 2n (или kn для любого k) слишком мал, чтобы удерживать выход в целом. – chepner

+0

как бы вы использовали буфер длиной 2n? Вы не можете предопределить размер набора. – committedandroider

ответ

3

Для временной сложности, я был в состоянии показать, что алгоритм работает в O (N) с рекуррентным соотношением

Это неправильно. Число последовательностей сбалансированных круглых скобок задается Catalan numbers: их число экспоненциально экспоненциально. Ваш алгоритм не может быть линейным, если он также правильно решает проблему, потому что просто вывод экспоненциального числа решений сам принимает экспоненциальное время.

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

Вы можете решить эту проблему без использования экспоненциальной памяти: подумайте о том, как вы можете избавиться от хранения всех предыдущих последовательностей.

+0

Вам не нужно было бы хранить все предыдущие последовательности, чтобы применить алгоритм? Если вы их не храните, нет способа получить доступ ко всем строкам и сформировать все комбинации круглых скобок. – committedandroider

+0

@committedandroider - на каждом шаге у вас есть две возможности: поместите открытую скобку или закрытую. Выполняя это так, вы сохраняете использование памяти в «O (n)». Это также создаст несбалансированные последовательности, но если вы выясните, как заставить его генерировать только сбалансированные последовательности, все готово. Подсказка: отслеживайте, сколько вы открыли и сколько вы закрыли. – IVlad

+0

Я не понимаю, что вы имеете в виду. Как я вижу, эта проблема заключается в нахождении возможности поставить «()» до и после открытия круглой скобки. Скажем, у меня есть первый вызов функции f (1), который будет(). В этом случае, что вы подразумеваете под каждым шагом, поставьте открытую скобку или закрытую скобку? Разве вам не нужно было бы открывать скобу и закрытую скобу? – committedandroider

1

Числа способов, чтобы написать п пара надлежащим образом подобранных скобок является п го Каталонского числа, которое на самом деле растет экспоненциально, а не quadratical. Только пространственная сложность выхода равна O (2^n); см. the wikipedia article для краткого обзора каталонских номеров.

Обратите внимание, что вы не делаете ни одного рекурсивного вызова на каждой глубине, но, возможно, O (n) рекурсивных вызовов.

+0

Что вы подразумеваете под «уведомлением о том, что вы не делаете ни одного рекурсивного вызова на каждой глубине»? к этой функции с 4, то это вызовет 3, которые вызовут 2, тогда так ... Разве это не единственный рекурсивный вызов на каждой глубине? – committedandroider

+0

Для вызова с * n *, вы не обязательно совершаете только один вызов с помощью * n-1 *, вы можете сделать несколько таких вызовов. Это приведет к увеличению количества пространства, используемого алгоритмом, намного быстрее, хотя никогда не бывает больше O (n) * активных * вызывает одно время. – chepner

+0

Итак, на глубине 4 вы фактически делаете всего 4 рекурсивных вызова, f (3), f (2), f (1) и f (0)? – committedandroider

Смежные вопросы