
Динамический стек
Динамический стек — это линейный односвязный список. Работа с этим списком осуществляется по соответствующему принципу: новый элемент кладется на вершину стека, то есть вставляется перед первым элементом списка, и при необходимости взять элемент из стека выбирается значение первого элемента, указатель стека смещается на следующий элемент, а "использованный" элемент удаляется.
Стек реализован в виде класса, поля и методы которого представлены в листинге 8.22.
Листинг 8.22. Класс стека, реализованный динамически
class MyStack
{
Node top;
public void Push(object data) // положить в стек
public object Pop() // взять из стека
public bool IsEmpty() // проверка на пустоту
class Node // узел
}
Развернутый вариант кода класса:
Листинг 8.23. Класс стека, реализованный динамически
namespace SpaceStack
{
class MyStack
{
Node top;
public void Push(object data)
//положить в стек
{
top = new Node(top, data);
}
public object Pop() // взять из стека
{
if (top == null) throw new
InvalidOperationException();
object result = top.data;
top = top.next;
return result;
}
public bool isEmpty() // проверка на пустоту
{
return top == null;
}
class Node // узел
{
public Node next;
public object data;
// конструктор
public Node(Node next, object data)
{
this.next = next;
this.data = data;
}
}
}
}
Следует обратить внимание на то, что метод Pop() — извлечения элемента из стека — внутри себя не делает проверки на пустоту стека. Дело в том, что эта метод должна вызываться только в том случае, когда заранее известно, что стек не пуст. В том случае, когда проверкой (метод IsEmpty) установлено, что стек пуст, решение о дальнейших действиях должно приниматься в вызывающем контексте.
В примере объявляется класс MyStack, принадлежащий пространству имен SpaceStack. Полное имя этого класса — SpaceStack.Stack. Этот класс содержит несколько членов: поле top, два метода Push и Pop, а также вложенный класс Node. Класс Node в свою очередь содержит три члена: поля next и data, а также конструктор. Если исходный код примера хранится в файле MyStack.cs, команда
csc /t:library MyStack.cs
компилирует текст в виде библиотеки, если код не имеет метода Main() и создает сборку с именем MyStack.dll.
В программе C# возможность обращения к открытым типам и членам, содержащимся в конкретной сборке, реализуется посредством ссылки на эту сборку во время компиляции программы. Если в программе используется класс SpaceStack.MyStack, содержащийся в сборке MyStack.dll , то можно решить следующую задачу.
Пример 8.1. Для иллюстрации вышесказанного решим следующую задачу: проверить правильность расстановки скобок в арифметическом выражении.
По правилам записи арифметических выражений первой должна закрываться последняя открывающая скобка, и число открывающих и закрывающих скобок должно совпадать. Будем считать, что в выражении используются три вида скобок: круглые, квадратные и фигурные.
Примечание. Не составит труда, изменив тип информационного поля на string, добавить еще несколько видов скобок, например, операторные скобки begin и end.
Ошибки, которые могут встретиться при расстановке скобок:
несоответствие открывающей и закрывающей скобок, например: {a×[b+c)};
непарные скобки, то есть открывающих скобок больше, чем закрывающих (или наоборот).
Поэтому алгоритм проверки должен быть следующим. Выражение просматривается посимвольно, и если встретилась любая открывающая скобка, то она помещается в стек. Если встретилась закрывающая скобка, то из стека берется последняя открывающая и проверяется, соответствует ли она закрывающей. Просмотр выражения заканчивается либо когда обнаружится ошибка, либо когда строка закончится. Если строка закончилась, то необходимо еще проверить стек — не остались ли там непарные открывающие скобки.
Метод BracketTest, приведенная в листинге 8.24, реализует этот алгоритм и возвращает значения:
–1, если скобки расставлены правильно;
0, если не хватает закрывающих скобок;
номер позиции в строке, на которой стоит "неправильная" скобка.
Листинг 8.24. Проверка скобок в арифметическом выражении
static int BracketTest(string a)
{
char[] begBracket = { '(', '[', '{' };
char[] endBracket = { ')', ']', '}' };
MyStack myStack = new MyStack();
int lenA = a.Length;
bool error = false;
int result = -1;
int i = -1;
do
{
if (isBracket(begBracket, a[++i]))
myStack.Push(a[i]);
else
if (isBracket(endBracket, a[i]))
if (!myStack.isEmpty())
switch (myStack.Pop())
{
case '(':
if (a[i]!=')')
{result =i; error = true;}
break;
case '[':
if (a[i]!=']')
{result =i; error = true;}
break;
case '{':
if (a[i]!='}')
{result =i; error = true;}
break;
}
else
{ error = true; result = i; }
}
while (!error && (i != lenA-1));
if (!myStack.isEmpty() || error)
result = i;
return result;
}
Для вызова процедуры достаточно написать одну строчку кода:
Console.WriteLine(BracketTest("{aaa(bbb)cc[ss[]}"));
Для данной строки на экране будет напечатано число 17. Это означает, что в 17-ой позиции строки ошибка – напечатана скобка }, а должна быть сначала скобка ].