Смекни!
smekni.com

VB, MS Access, VC++, Delphi, Builder C++ принципы(технология), алгоритмы программирования (стр. 13 из 72)

' Структура данных ячейки.

Type FakeCell

Value As String

NextCell As Integer

End Type

' Массив ячеек связного списка.

Global Cells(0 To 100) As FakeCell

' Сигнальная метка списка.

Global Sentinel As Integer

Поскольку псевдоуказатели — это не ссылки, а просто целые числа, программа не может использовать значение Nothing для маркировки конца списка. Программа FakeList использует постоянную END_OF_LIST, значение которой равно -32.767 для обозначения пустого указателя.

Для облегчения обнаружения неиспользуемых ячеек, программа FakeList также использует специальный «мусорный» список, содержащий неиспользуемые ячейки. Следующий код демонстрирует инициализацию пустого связного списка. В нем сигнальная метка NextCell принимает значение END_OF_LIST. Затем она помещает неиспользуемые ячейки в «мусорный» список.

========44

' Связный список неиспользуемых ячеек.

Global TopGarbage As Integer

Public Sub InitializeList()

Dim i As Integer

Sentinel = 0

Cells(Sentinel).NextCell = END_OF_LIST

' Поместить все остальные ячейки в «мусорный» список.

For i = 1 To UBound (Cells) - 1

Cells(i).NextCell = i + 1

Next i

Cells(UBound(Cells)).NextCell = END_OF_LIST

TopGarbage = 1

End Sub

При добавлении элемента к связному списку, программа использует первую доступную ячейку из «мусорного» списка, инициализирует поле ячейки Value и вставляет ячейку в список. Следующий код показывает, как программа добавляет элемент после выбранного:

Private Sub CmdAddAfter_Click()

Dim ptr As Integer

Dim position As Integer

Dim new_cell As Integer

' Найти место вставки.

ptr = Sentinel

position = Selectedlndex

Do While position > 0

position = position - 1

ptr = Cells(ptr).NextCell

Loop

' Выбрать новую ячейку из «мусорного» списка.

new_cell = TopGarbage

TopGarbage = Cells(TopGarbage).NextCell

' Вставить элемент.

Cells (new_cell).Value = NewItem.Text

Cells(new_cell).NextCell = Cells(ptr).NextCell

Cells(ptr).NextCell = new_cell

NumItems = NumItems + 1

DisplayList

SelectItem SelectedIndex + 1 ' Выбрать новый элемент.

NewItem.Text = ""

NewItem.SetFocus

CmdClearList.Enabled = True

End Sub

После удаления ячейки из списка, программа FakeList помещает удаленную ячейку в «мусорный» список, чтобы ее затем можно было легко использовать:

Private Sub CmdRemoveAfter_Click()

Dim ptr As Integer

Dim target As Integer

Dim position As Integer

If SelectedIndex < 0 Then Exit Sub

' Найти элемент.

ptr = Sentinel

position = SelectedIndex

Do While position > 0

position = position - 1

ptr = Cells(ptr).NextCell

Loop

' Пропустить следующий элемент.

target = Cells(ptr).NextCell

Cells(ptr).NextCell = Cells(target).NextCell

NumItems = NumItems - 1

' Добавить удаленную ячейку в «мусорный» список.

Cells(target).NextCell = TopGarbage

TopGarbage = target

SelectItem Selectedlndex ' Снова выбрать элемент.

DisplayList

CmdClearList.Enabled = NumItems > 0

NewItem.SetFocus

End Sub

Применение псевдоуказателей обычно обеспечивает лучшую производительность, но является более сложным. Поэтому имеет смысл сначала создать приложение, используя ссылки на объекты. Затем, если вы обнаружите, что программа значительную часть времени тратит на манипулирование ссылками, вы можете, если необходимо, преобразовать ее с использованием псевдоуказателей.

=======45-46

Резюме

Используя ссылки на объекты, вы можете создавать гибкие структуры данных, такие как связные списки, циклические связные списки и двусвязные списки. Эти списки позволяют легко добавлять и удалять элементы из любого места списка.

Добавляя дополнительные ссылки к классу ячеек, можно превратить двусвязный список в многопоточный. Развивая и дальше эти идеи, можно создавать экзотические структуры данных, включая разреженные массивы, деревья, хэш‑таблицы и сети. Они подробно описываются в следующих главах.

========47

Глава 3. Стеки и очереди

В этой главе продолжается обсуждение списков, начатое во 2 главе, и описываются две особых разновидности списков: стеки и очереди. Стек — это список, в котором добавление и удаление элементов осуществляется с одного и того же конца списка. Очередь — это список, в котором элементы добавляются в один конец списка, а удаляются с противоположного конца. Многие алгоритмы, включая некоторые из представленных в следующих главах, используют стеки и очереди.

Стеки

Стек (stack) — это упорядоченный список, в котором добавление и удаление элементов всегда происходит на одном конце списка. Можно представить стек как стопку предметов на полу. Вы можете добавлять элементы на вершину и удалять их оттуда, но не можете добавлять или удалять элементы из середины стопки.

Стеки часто называют списками типа первый вошел — последний вышел (Last‑In‑First‑Out list). По историческим причинам, добавление элемента в стек называется проталкиванием (pushing) элемента в стек, а удаление элемента из стека — выталкиванием (popping) элемента из стека.

Первая реализация простого списка на основе массива, описанная в начале 2 главы, является стеком. Для отслеживания вершины списка используется счетчик. Затем этот счетчик используется для вставки или удаления элемента из вершины списка. Небольшое изменение — это новая процедура Pop, которая удаляет элемент из списка, одновременно возвращая его значение. При этом другие процедуры могут извлекать элемент и удалять его из списка за один шаг. Кроме этого изменения, следующий код совпадает с кодом, приведенным во 2 главе.

Dim Stack() As Variant

Dim StackSize As Variant

Sub Push(value As Variant)

StackSize = StackSize + 1

ReDim Preserve Stack(1 To StackSize)

Stack(StackSize) = value

End Sub

Sub Pop(value As Variant)

value = Stack(StackSize)

StackSize = StackSize - 1

ReDim Preserve Stack(1 To StackSize)

End Sub

=====49

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

Программы часто используют стеки для хранения последовательности элементов, с которыми программа будет работать до тех пор, пока стек не опустеет. Действия с одним из элементов может приводить к тому, что другие будут проталкиваться в стек, но, в конце концов, они все будут удалены из стека. В качестве простого примера можно привести алгоритм обращения порядка элементов массива. При этом все элементы последовательно проталкиваются в стек. Затем все элементы выталкиваются из стека в обратном порядке и записываются обратно в массив.

Dim List() As Variant

Dim NumItems As Integer

' Инициализация массива.

:

' Протолкнуть элементы в стек.

For I = 1 To NumItems

Push List(I)

Next I

' Вытолкнуть элементы из стека обратно в массив.

For I = 1 To NumItems

Pop List(I)

Next I

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

Следующий код позволяет создать стек, если заранее известен его максимальный размер. Процедура Pop не изменяет размер массива. Когда программа заканчивает работу со стеком, она должна вызвать процедуру EmptyStack для освобождения занятой под стек памяти.

======50

Const WANT_FREE_PERCENT = .1 ' 10% свободного пространства.

Const MIN_FREE = 10 ' Минимальный размер.

Global Stack() As Integer ' Стековый массив.

Global StackSize As Integer ' Размер стекового массива.

Global Lastltem As Integer ' Индекс последнего элемента.

Sub PreallocateStack(entries As Integer)

StackSize = entries

ReDim Stack(1 To StackSize)

End Sub

Sub EmptyStack()

StackSize = 0

LastItem = 0

Erase Stack ' Освободить память, занятую массивом.

End Sub

Sub Push(value As Integer)

LastItem = LastItem + 1

If LastItem > StackSize Then ResizeStack

Stack(LastItem) = value

End Sub

Sub Pop(value As Integer)

value = Stack(LastItem)

LastItem = LastItem - 1

End Sub

Sub ResizeStack()

Dim want_free As Integer

want_free = WANT_FREE_PERCENT * LastItem

If want_free < MIN_FREE Then want_free = MIN_FREE

StackSize = LastItem + want_free

ReDim Preserve Stack(1 To StackSize)

End Sub

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

=======51

Множественные стеки

В одном массиве можно создать два стека, поместив один в начале массива, а другой — в конце. Для двух стеков используются отдельные счетчики длины стека Top, и стеки растут навстречу друг другу, как показано на рис. 3.1. Этот метод позволяет двум стекам расти, занимая одну и ту же область памяти, до тех пор, пока они не столкнутся, когда массив заполнится.

К сожалению, менять размер этих стеков непросто. При увеличении массива необходимо сдвигать все элементы в верхнем стеке, чтобы выделять память под новые элементы в середине. При уменьшении массива, необходимо вначале сдвинуть элементы верхнего стека, перед тем, как менять размер массива. Этот метод также сложно масштабировать для оперирования более чем двумя стеками.

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

Основной недостаток применения стеков на основе связных списков состоит в том, что они требуют дополнительной памяти для хранения указателей NextCell. Для стека на основе массива, содержащего N элементов, требуется всего 2*N байт памяти (по 2 байта на целое число). Тот же стек, реализованный на основе связного списка, потребует дополнительно 4*N байт памяти для указателей NextCell, увеличивая размер необходимой памяти втрое.

Программа Stack использует несколько стеков, реализованных в виде связных списков. Используя программу, можно вставлять и выталкивать элементы из каждого из этих списков. Программа Stack2 аналогична этой программе, но она использует класс LinkedListStack для работы со стеками.