Главная Промышленная автоматика.

будут находиться в порядке е, е, е„, когда ei < ег < вз < ... < с„. Преимущество отсортированного списка заключается в том, что для нахождения конкретного элемента в списке нет необходимости просматривать весь список.

Элемент будет принадлежать пересечению списков и L2 тогда и только тогда, когда он содержится в обоих списках. В случае несортированных списков мы должны сравнить каждый элемент списка Li с каждым элементом списка L2, т.е. сделать порядка О(п) операций при работе со списками длины п. Для сортированных списков операторы пересечения и некоторые другие выполняются сравнительно просто: если надо сравнить элемент е списка с элементами списка L2, то надо просматривать список La только до тех пор, пока не встретится элемент е или больщий, чем е. В первом случае будет совпадение элементов, второй случай показывает, что элемента е нет в списке L2. Более интересна ситуация, когда мы знаем элемент d, который в списке Li непосредственно предществует элементу е. Тогда для поиска элемента, совпадающего с элементом е, в списке L2 можно сначала найти такой элемент /, что d < f, и начать просмотр списка L2 с этого элемента. Используя этот прием, можно найти совпадающие элементы в списках Li и L2 за один проход этих списков, продвигаясь вперед по спискам в прямом порядке, начиная с наименьщего элемента. Код процедуры, реализующей оператор INTERSECTION, показан в листинге 4.3. Здесь множества представлены связанными списками ячеек, чей тип определяется следующим образом:

type

celltype = record

eienient: elementtype; next: tcelltype

В листинге 4.3 предполагается, что elementtype - это тип целых чисел, которые можно упорядочить посредством обычного оператора сравнения <. Если elementtype представлен другим типом данных, то надо написать функцию, которая будет определять, какой из двух заданных элементов предществует другому элементу.

Листинг 4.3. Процедура INTERSECTION, использующая связанные списки

procedure INTERSECTION ( ahead, bhead: Tcelltype; var pc: Tcelltype ) ,-{ Вычисление пересечения сортированных списков А и В с ячейками заголовков ahead и bhead, результат - сортированный список, на чей заголовок указывает рс }

acurrent, bcurrent, ccurrent: Tcelltype; { текущие ячейки списков и В и последняя ячейка дополнительного списка С }

begin

(1) new(pc); { создание заголовка для списка С }

(2) acurrent: = aheadt. next;

(3) bcurrent:= bheadt.next;

(4) ccurrent:= pc;

(5) while (acurrent <> nil) and (bcurrent <> nil) do begin

{ сравнение текущих элементов списков А и В }

(6) if аcurrentt. element = bcurrentt. element then begin

{ добавление элемента в пересечение }

(7) new(ccurrentT.next) ;

(8) ccurrent:= ccurrentt.next;

(9) ccurrentT.elenient:= acurrent?. element;

(10) acurrent: = acurrentt. next;



(11) bcurrent:= bcurrentt. next end

else { элементы неравны }

(12) if acurrentt. element < bcurrentT. element then

(13) acurrent:= acurrentT. next; else

(14) bcurrent:= bcurrentt. next end

(15) ccurrentT. next: = nil

end; { INTERSECTION }

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

Процедуру INTERSECTION листинга 4.3 можно легко приспособить для реализации операторов UNION и DIFFERENCE. Для выполнения оператора UNION надо все элементы из списков А и В записать в список С. Поэтому, когда элементы не равны (строки 12-14 в листинге 4.3), наименьший из них заносится в список С, так же, как и в случае их равенства. Элементы заносятся в список С до тех пор, пока на исчерпаются оба списка, т.е. пока логическое выражение в строке 5 не примет значение false. В процедуре DIFFERENCE в случае равенства элементов они не заносятся в список С. Если текущий элемент списка А меньще текущего элемента списка В, то он (текущий элемент списка А) заносится в список С. Элементы заносятся в список С до тех пор, пока не исчерпается список (логическое условие в строке 5).

Оператор ASSIGN (А, В) копирует список А в список В. Отметим, что этот оператор нельзя реализовать простым переопределением заголовка списка В на заголовок списка А, поскольку при последующих изменениях в списке В надо будет делать аналогичные изменения в списке А, что, естественно, может привести к нежелательным коллизиям. Оператор MIN реализуется легко - просто возвращается первый элемент списка. Операторы DELETE и FIND можно реализовать, применив общие методы поиска заданного элемента в списках, в случае оператора DELETE найденный элемент (точнее, ячейка, в которой он находится) удаляется.

Реализовать оператор вставки нового элемента в список таюке неслокно, но он должен стоять не в произвольной позиции в списке, а в "правильной" позиции, учитывающей взаимный порядок элементов. В листинге 4.4 представлен код процедуры INSERT (Вставка), которая в качестве параметров имеет вставляемый элемент и указатель на ячейку заголовка списка, куда вставляется элемент. На рис. 4.2 показаны ключевые ячейки и указатели до и после вставки элемента (старые указатели обозначены сплошными линиями, а новые - пунктирными).

Листинг 4.4. Процедура вставки элемента

procedure INSERT ( х: elementtype; р: tcelltype ) ; var

current, newcell: Tcelltype; begin

current:= p;

while currentT.next <> nil do begin

if currentt. nextt. element = x then

return; { элемент x уже есть в списке } if currentT.nextt. element > x then



add:

end;

goto add; { далее останов процедуры } current:= current?, next

end;

{здесь current - ячейка, после которой надо вставить х} new{newcell) ; newcellT. element:= х; newcellT. next: = current?.next; current?.next:= newcell

{ INSERT }

1----------:--£

current

newcell

t

----

Puc. 4.2. Схема вставки нового элемента

4.5. Словари

Применение множеств при разработке алгоритмов не всегда требует таких мощных операторов, как операторы обьединения и пересечения. Часто достаточно только хранить в множестве "текущие" обьекты с периодической вставкой или удалением некоторых из них. Время от времени также возникает необходимость узнать, присутствует ли конкретный элемент в данном множестве. Абстрактный тип множеств с операторами INSERT, DEEETE и MEMBER называется DICTIONARY (Словарь). Мы также включим оператор MAKENULL в набор операторов словаря - он потребуется при реализации АТД для инициализации структур данных. Далее в этом разделе мы приведем пример использования словарей, а в следующем разделе рассмотрим реализации, подходящие для представления словарей.

Пример 4.2. Общество защиты тунцов (ОЗТ) имеет базу данных с записями результатов самого последнего голосования законодателей по законопроектам об охране тунцов. База данных состоит из двух списков (множеств) имен законодателей, которые названы goodguys (хорощие парни) и badguys (плохие парни). ОЗТ прощает законодателям их прощдые "ощибки", но имеет тенденцию забывать своих "друзей", которые ранее голосовали "правильно". Например, после голосования по законопроекту об ограничении вылова тунца в озере Эри все законодатели, проголосовавщие за этот законопроект, заносятся в список goodguys и удаляются из списка badguys, тогда как над оппонентами этого законопроекта совершается обратная процедура. За-

4.5. СЛОВАРИ





0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 [33] 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123

0.0036