Возможный дубликат:
Решение рекурсивной проблемы (код ката)
дать алгоритм для нахождения всех допустимых перестановок скобок для данного n например:
for n=3, O/P should be
{}{}{}
{{{}}}
{{}}{}
{}{{}}
{{}{}}
Возможный дубликат:
Решение рекурсивной проблемы (код ката)
дать алгоритм для нахождения всех допустимых перестановок скобок для данного n например:
for n=3, O/P should be
{}{}{}
{{{}}}
{{}}{}
{}{{}}
{{}{}}
Это классическая комбинаторная проблема, которая проявляется по-разному. Эти проблемы по существу идентичны:
N
(т.е. этой проблемы)N+1
факторамN+1
оставляетЗдесь простой рекурсивный алгоритм для решения этой проблемы в Java:
public class Parenthesis {
static void brackets(int openStock, int closeStock, String s) {
if (openStock == 0 && closeStock == 0) {
System.out.println(s);
}
if (openStock > 0) {
brackets(openStock-1, closeStock+1, s + "<");
}
if (closeStock > 0) {
brackets(openStock, closeStock-1, s + ">");
}
}
public static void main(String[] args) {
brackets(3, 0, "");
}
}
Вышеприведенные отпечатки (как видно на ideone.com):
<<<>>>
<<><>>
<<>><>
<><<>>
<><><>
По существу, мы отслеживаем, сколько открытых и закрытых круглых скобок "доступно" для использования нами при построении строки рекурсивно.
Обратите внимание: если вы поменяете порядок рекурсии таким образом, что вы пытаетесь добавить закрывающую скобку, прежде чем пытаться добавить открытую скобку, вы просто получите тот же список сбалансированных круглых скобок, но в обратном порядке! (см. на ideone.com).
Вышеупомянутое решение является очень простым и поучительным, но может быть оптимизировано далее.
Самая важная оптимизация - в аспекте построения строки. Хотя это выглядит как простая конкатенация строк на поверхности, в приведенном выше решении фактически есть "скрытый" компонент построения строки O(N^2)
(поскольку конкатенация одного символа на неизменяемый String
длины N
является операцией O(N)
), Как правило, мы оптимизируем это, используя вместо этого переменный StringBuilder
, но для этого конкретного случая мы также можем просто использовать переменную char[]
и index
с фиксированным размером.
Мы также можем оптимизировать, упростив дерево рекурсии. Вместо того, чтобы рекурсировать "оба пути", как в исходном решении, мы можем просто повторить "один путь" и сделать "другой путь" итеративно.
В дальнейшем мы выполнили обе оптимизации, используя char[]
и index
вместо String
, и рекурсивно добавляем открытые круглые скобки, добавляя тесные круглые скобки итеративно: (см. также на ideone.com)
public class Parenthesis2 {
public static void main(String[] args) {
brackets(4);
}
static void brackets(final int N) {
brackets(N, 0, 0, new char[N * 2]);
}
static void brackets(int openStock, int closeStock, int index, char[] arr) {
while (closeStock >= 0) {
if (openStock > 0) {
arr[index] = '<';
brackets(openStock-1, closeStock+1, index+1, arr);
}
if (closeStock-- > 0) {
arr[index++] = '>';
if (index == arr.length) {
System.out.println(arr);
}
}
}
}
}
Логика рекурсии теперь менее очевидна, но две методики оптимизации являются поучительными.
Пока не является фактическим алгоритмом, хорошей отправной точкой является каталонский номер:
Ссылка
Эрик Липперт недавно написал об этом в своей статье Every Tree There. Статья ссылается на код, написанный в предыдущей статье Every Binary Tree There.
Если вы можете перечислить все двоичные деревья, то оказывается, что вы можете перечислить все решения десятков различных эквивалентных задач.
Нерекурсивное решение в Python:
#! /usr/bin/python
def valid(state,N):
cnt=0
for i in xrange(N):
if cnt<0:
return False
if (state&(1<<i)):
cnt+=1
else:
cnt-=1
return (cnt==0)
def make_string(state,N):
ret=""
for i in xrange(N):
if state&(1<<i):
ret+='{'
else:
ret+='}'
return ret
def all_permuts(N):
N*=2
return [make_string(state,N) for state in xrange(1<<N) if valid(state,N)]
if __name__=='__main__':
print "\n".join(all_permuts(3))
Это в основном рассматривает двоичное представление каждого числа в [0,2 ^ n), рассматривая "1" как "{" и "0" как "}", а затем отфильтровывает только те, которые должным образом сбалансирован.