Ремонт принтеров, сканнеров, факсов и остальной офисной техники


назад Оглавление вперед




[49]

Сколько шагов мы на этом выигрываем? В худшем случае, объект, который мы ищем, может быть наибольшим в множестве, так что число шагов то же, что и для неупорядоченного представления. С другой стороны, если мы ищем элементы разных размеров, можно ожидать, что иногда мы сможем останавливаться близко к началу списка, а иногда нам все же потребуется просмотреть большую его часть. В среднем мы можем ожидать, что потребуется просмотреть около половины элементов множества. Таким образом, среднее число требуемых шагов будет примерно n/2. Это все еще рост порядка 0(n), но это экономит нам в среднем половину числа шагов по сравнению с предыдущей реализацией.

Более впечатляющее ускорение мы получаем в intersection-set. При неупорядоченном представлении эта операция требовала 0(n2) шагов, поскольку мы производили полный поиск в set2 для каждого элемента setl. Однако при упорядоченном представлении мы можем воспользоваться более разумным методом. Начнем со сравнения первых элементов двух множеств, xl и x2. Если xl равно x2, мы получаем один элемент пересечения, а остальные элементы пересечения мы можем получить, пересекая оставшиеся элементы списков-множеств. Допустим, однако, что xl меньше, чем x2. Поскольку x2 - наименьший элемент set2, мы можем немедленно заключить, что xl больше нигде в set2 не может встретиться и, следовательно, не принадлежит пересечению. Следовательно пересечение двух множеств равно пересечению set2 с cdr от setl. Подобным образом, если x2 меньше, чем xl, то пересечение множеств получается путем пересечения setl с cdr от set2. Вот процедура:

(define (intersection-set setl set2)

(if (or (null? setl) (null? set2))

()

(let ((xl (car setl)) (x2 (car set2))) (cond ((= xl x2)

(cons xl

(intersection-set (cdr setl)

(cdr set2))))

((< xl x2)

(intersection-set (cdr setl) set2)) ((< x2 xl)

(intersection-set setl (cdr set2)))))))

Чтобы оценить число шагов, необходимое для этого процесса, заметим, что на каждом шагу мы сводим задачу нахождения пересечения к вычислению пересечения меньших множеств - убирая первый элемент либо из setl, либо из set2, либо из обоих. Таким образом, число требуемых шагов не больше суммы размеров setl и set2, а не их произведения, как при неупорядоченном представлении. Это рост 0(n), а не 0(n2) - заметное ускорение, даже для множеств небольшого размера.

Упражнение 2.61.

Напишите реализацию adjoin-set для упорядоченного представления. По аналогии с element-of-set? покажите, как использовать упорядочение, чтобы получить процедуру, которая в среднем требует только половину того числа шагов, которое требуется при неупорядоченном представлении.

Упражнение 2.62.

Дайте представление порядка @(п) для операции union-set с представлением в виде


735

11

Рис. 2.16: Различные бинарные деревья, представляющие множество {1,3, 5,7, 9,11}.

упорядоченных списков.

Множества как бинарные деревья

Можно добиться еще лучших результатов, чем при представлении в виде упорядоченных списков, если расположить элементы множества в виде дерева. Каждая вершина дерева содержит один элемент множества, называемый «входом» этой вершины, и указатели (возможно, пустые) на две другие вершины. «Левый» указатель указывает на элементы, меньшие, чем тот, который содержится в вершине, а «правый» на элементы, большие, чем тот, который содержится в вершине. На рисунке 2.16 показано несколько вариантов представления множества {1, 3, 5, 7, 9, 11} в виде дерева. Одно и то же множество может быть представлено в виде дерева несколькими различными способами. Единственное, чего мы требуем от правильного представления - это чтобы все элементы левого поддерева были меньше, чем вход вершины, а элементы правого поддерева больше.

Преимущество древовидного представления следующее: Предположим, мы хотим проверить, содержится ли в множестве число x. Начнем с того, что сравним x со входом начальной вершины. Если x меньше его, то мы уже знаем, что достаточно просмотреть только левое поддерево; если x больше, достаточно просмотреть правое поддерево. Если дерево «сбалансировано», то каждое из поддеревьев будет по размеру примерно вполовину меньше. Таким образом, за один шаг мы свели задачу поиска в дереве размера n к задаче поиска в дереве размера n/2. Поскольку размер дерева уменьшается вдвое на каждом шаге, следует ожидать, что число шагов, требуемых для поиска в дереве размера n, растет как 0(logn).38 Для больших множеств это будет заметным ускорением по сравнению с предыдущими реализациями.

Деревья мы можем представлять при помощи списков. Каждая вершина будет списком из трех элементов: вход вершины, левое поддерево и правое под-

38Уменьшение размера задачи вдвое на каждом шагу является определяющей характеристикой логарифмического роста, как мы видели на примере алгоритма быстрого возведения в степень в разделе 1.2.4 и метода половинного деления в разделе 1.3.3.


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

39

представление при помощи следующих процедур:39

(define (entry tree) (car tree))

(define (left-branch tree) (cadr tree))

(define (right-branch tree) (caddr tree))

(define (make-tree entry left right) (list entry left right))

Теперь можно написать процедуру element-of-set? с использованием вышеописанной стратегии:

(define (element-of-set? x set) (cond ((null? set) false)

((= x (entry set)) true) ((< x (entry set))

(element-of-set? x (left-branch set))) ((> x (entry set)) (element-of-set? x (right-branch set)))))

Добавление элемента к множеству реализуется похожим образом и также требует 0(log n) шагов. Чтобы добавить объект x, мы сравниваем его с входом вершины и определяем, должны ли мы добавить x к левой или правой ветви, а добавив x к соответствующей ветви, мы соединяем результат с изначальным входом и второй ветвью. Если x равен входу, мы просто возвращаем вершину. Если нам требуется добавить x к пустому дереву, мы порождаем дерево, которое содержит x на входе и пустые левое и правое поддеревья. Вот процедура:

(define (adjoin-set x set)

(cond ((null? set) (make-tree x () ())) ((= x (entry set)) set) ((< x (entry set)) (make-tree (entry set)

(adjoin-set x (left-branch set)) (right-branch set))) ((> x (entry set)) (make-tree (entry set)

(left-branch set)

(adjoin-set x (right-branch set))))))

Утверждение, что поиск в дереве можно осуществить за логарифмическое число шагов, основывается на предположении, что дерево «сбалансировано», то есть что левое и правое его поддеревья содержат приблизительно одинаковое число элементов, так что каждое поддерево содержит приблизительно половину элементов своего родителя. Но как нам добиться того, чтобы те деревья,

39Мы представляем множества при помощи деревьев, а деревья при помощи списков - получается абстракция данных на основе другой абстракции данных. Процедуры entry, left-branch, right-branch и make-tree мы можем рассматривать как способ изолировать абстракцию «бинарное дерево» от конкретного способа, которым мы желаем представить такое дерево в виде списковой структуры.



[стр.Начало] [стр.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] [стр.124] [стр.125] [стр.126] [стр.127] [стр.128] [стр.129] [стр.130] [стр.131] [стр.132] [стр.133] [стр.134] [стр.135] [стр.136] [стр.137] [стр.138] [стр.139] [стр.140] [стр.141] [стр.142] [стр.143] [стр.144] [стр.145] [стр.146] [стр.147] [стр.148] [стр.149] [стр.150] [стр.151] [стр.152] [стр.153] [стр.154] [стр.155] [стр.156] [стр.157] [стр.158] [стр.159] [стр.160] [стр.161] [стр.162] [стр.163] [стр.164] [стр.165] [стр.166] [стр.167] [стр.168] [стр.169] [стр.170] [стр.171] [стр.172] [стр.173] [стр.174] [стр.175] [стр.176] [стр.177] [стр.178] [стр.179] [стр.180] [стр.181] [стр.182] [стр.183] [стр.184] [стр.185] [стр.186] [стр.187] [стр.188] [стр.189] [стр.190] [стр.191] [стр.192] [стр.193] [стр.194] [стр.195] [стр.196]