Стеки. Часть 2. Расстановка скобок
В первой части цикла статей был реализован класс стека. Сейчас мы рассмотрим применение стека для решения одной, в общем-то, классической задачи по программированию.
Задача
Необходимо проверить правильность расстановки скобок "(), {}, []" в выражении. То есть проверить, все ли скобки закрыты, соответствует ли закрывающаяся скобка открывающейся, нет ли лишних закрывающихся скобок и тд.
К примеру, такое выражение 2 * ( 3 – [ x+ 3 ] / 15 ) – { 2 + 3 } * 5 проверку пройдет, а 2 * ( 3 – [ x+ 3 ) / 15 ] – { 2 + 3 } * 5 уже нет.
Алгоритм решения
Рассмотрим сначала общий словесный алгоритм решения. Полагаю, можно обойтись без псевдокодов и блок-схем.
- Проходим посимвольно все выражение
-
Проверяем, является ли текущий символ скобкой
- Если эта скобка открывающаяся, тогда кладем ее в стек
-
Если же нет, тогда, проверяем, есть ли в стеке элементы
- Если да, тогда находим закрывающуюся скобку, для той, которая лежит на вершине стека. Проверяем ее с текущей скобкой, если они не равны, то ошибка. Если равны, то выталкиваем скобку из стека.
- Если же стек пуст, тогда получаем, что нашли закрывающуюся скобку, хотя позади нет ни одной открытой, следовательно, ошибка
- После того, как прошли выражение, проверяем, остались ли в стеке скобки, если остались, следовательно, не все скобки закрыты
В общем, смысл в том, что все открывающиеся скобки мы скидываем в стек, и как только встретится закрывающиеся, проверяем является ли она парой, той, которая на вершине стека.
Для лучшего понимания это можно рассмотреть на примере.
Возьмем выражение 2 * ( 3 – [ x + 3 ] / 15 }– { 2 + 3 }* 5].
Находим первую открывающееся скобку, это будет «(», кладем ее в стек, идем далее. Доходим до скобки «[», тоже кладем ее в стек. К тому времени, как мы дойдем до скобки «]», стек уже будет «(, [», проверяем вершину стека: «[» и скобку «]», как видно они пара. Следовательно скобку [ выталкиваем из стека. Идем далее до скобки }. На это время в стеке у нас осталась только «(», очевидно, что парой для } она не является, получаем ошибку. На этом, в общем-то программу можно прервать, то лучше дойти до конца и получить полный список ошибок. Скобку { кладем в стек, после проверки со следующей }, так как они пара, то { можно вытолкнуть. В стеке опять осталась только (. Дошли до последней скобки ]. Снова получаем ошибку, так как ( и ], собственно не пара. И так же, поскольку после обхода, у нас в стеке все еще осталась скобка (, то получаем еще одну ошибку, что не все скобки закрыты.
Как видно, все не так страшно : )
Решение на Delphi
Для начала введем класс скобки. Вообще, без ООП в данной задаче вполне можно обойтись, но все, же так будет удобнее и красивее. Назовем класс TBracket.
У него будет два доступных только для чтения свойства типа char: Open и Close. Соответственно открывающаяся и закрывающаяся скобка. Тип открывающей скобки передается как параметр конструктору, в котором задается соответствующая закрывающаяся скобка.
- TBracket = class
- private
- FOpen: char;
- FClose: char;
- public
- constructor Create( setOpen: char );
- property Open: char read FOpen; // Открывающаяся скобка
- property Close: char read FClose; // Закрывающаяся скобка
- end;
- //...
- constructor TBracket.Create( setOpen: char );
- begin
- FOpen := setOpen;
- // Если setOpen не является открывающейся скобкой, получаем ошибку
- if not (setOpen in ['{','[','(']) then
- raise Exception.Create('Неизвестный вид скобок');
- // Находим парную скобку
- case setOpen of
- '(': FClose := ')';
- '[': FClose := ']';
- '{': FClose := '}';
- end;
- end;
Интерфейс приложения будет состоять из трех компонент:
- BracketEdit: TEdit– поле, куда вводится выражение для проверки
- CheckButton: TButton – кнопка, при нажатии на которую, проверка собственно и выполняется
- ErrorList: TListBox – поле, куда выводятся сообщения об ошибках.

В качестве поля формы нужно сделать объект Stack типа TStStack, класса стека разработанного нами ранее.
В обработчике события OnCreate формы нужно создать стек, а в OnDestroy уничтожить.
- procedure TMainForm.FormCreate(Sender: TObject);
- begin
- Stack := TStStack.Create;
- end;
- procedure TMainForm.FormDestroy(Sender: TObject);
- begin
- Stack.Free;
- end;
Введем функцию CheckBracket, она будет принимать строку для проверки, выполнять проверку, и возвращать список ошибок.
- function TMainForm.CheckBracket( CheckStr: string ): TStringList;
- var
- i: integer;
- ResultList: TStringList;
- begin
- // Очищаем стек
- Stack.Clear;
- // Создаем список для ошибок
- ResultList := TStringList.Create;
- // Посимвольно пробегаем выражение
- for i := 1 to Length( CheckStr ) do
- begin
- // Если символ не является скобкой, то дальнейший код не выполняется
- if not ( CheckStr[i] in ['{','[','(','}',']',')'] ) then
- Continue;
- // Если скобка открывающаяся
- if CheckStr[i] in ['{','[','('] then
- Stack.Push( TBracket.Create( CheckStr[i] ) ) // Кладем ее в стек
- else
- if Stack.Count > 0 then // Если стек не пуст выполняем проверку
- if TBracket(Stack.Peek).Close <> CheckStr[i] then
- ResultList.Add( 'Неcовместимый тип открывающей и закрывающей' +
- 'скобки ' + TBracket(Stack.Peek).Open + ' и ' + CheckStr[i] )
- else Stack.Pop.Free; // Выталкиваем скобку
- else // Если стек пуст, получаем ошибку
- ResultList.Add( 'Лишная закрывающая скобка: ' + CheckStr[i] );
- end;
- // Если после проверки остались открытые скобки,получаем ошибку
- if Stack.Count > 0 then
- ResultList.Add( 'Не все скобки закрыты!' );
- Result := ResultList;
- end;
Так же нужная функция ShowError, она принимает список ошибок и выводит их на экран.
- procedure TMainForm.ShowError( ErrList: TStringList );
- var
- i: integer;
- begin
- ErrorList.Items.Clear;
- if ErrList.Count > 0 then
- for i := 0 to ErrList.Count - 1 do
- ErrorList.Items.Add( ErrList[i] )
- else
- ErrorList.Items.Add( 'Ошибок не обнаружено' );
- end;
И наконец, теперь в обработчике события OnClick для кнопки CheckButton можно прописать следующее:
- ShowError( CheckBracket( BracketEdit.Text ) );
Теперь мы получили полностью работоспособное приложение.
Скачать исходные коды и exe файл можно здесь.
delphi, структуры данных, алгоритмы
Комментарии (0)
Добавить комментарий