на тему рефераты
 
Главная | Карта сайта
на тему рефераты
РАЗДЕЛЫ

на тему рефераты
ПАРТНЕРЫ

на тему рефераты
АЛФАВИТ
... А Б В Г Д Е Ж З И К Л М Н О П Р С Т У Ф Х Ц Ч Ш Щ Э Ю Я

на тему рефераты
ПОИСК
Введите фамилию автора:


Реферат: VB, MS Access, VC++, Delphi, Builder C++ принципы(технология), алгоритмы программирования


Деревья со ссылками

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

Для создания ссылок, указатели на предыдущий и следующий узлы в порядке симметричного обхода помещаются в неиспользуемых указателях на дочерние узлы. Если не используется указатель на левого потомка, то ссылка записывается на его место, указывая на предыдущий узел при симметричном обходе. Если не используется указатель на правого потомка, то ссылка записывается на его место, указывая на следующий узел при симметричном обходе. Поскольку ссылки симметричны, и ссылки левых потомков указывают на предыдущие, а правых — на следующие узлы, этот тип деревьев называется  деревом с симметричными ссылками (symmetrically threaded tree). На рис. 6.22 показано дерево с симметричными ссылками, которые обозначены пунктирными линиями.

Поскольку ссылки занимают место указателей на дочерние узлы дерева, нужно как‑то различать ссылки и обычные указатели на потомков. Проще всего добавить к узлам новые переменные HasLeftChild и HasRightChild типа Boolean, которые будут равны True, если узел имеет левого или правого потомка соответственно.

Чтобы использовать ссылки для поиска предыдущего узла, нужно проверить указатель на левого потомка узла. Если этот указатель является ссылкой, то ссылка указывает на предыдущий узел. Если значение указателя равно Nothing, значит это первый узел дерева, и поэтому он не имеет предшественников. В противном случае, перейдем по указателю к левому дочернему узлу. Затем проследуем по указателям на правый дочерний узел потомков, до тех пор, пока не достигнем узла, в котором на месте указателя на правого потомка находится ссылка. Этот узел (а не тот, на который указывает ссылка) является предшественником исходного узла. Этот узел является самым правым в левой от исходного узла ветви дерева. Следующий код демонстрирует поиск предшественника:

@Рис. 6.22. Дерево с симметричными ссылками

==========141

Private Function Predecessor(node As ThreadedNode) As ThreadedNode Dim child As ThreadedNode

    If node.LeftChild Is Nothing Then

        ' Это первый узел в порядке симметричного обхода.

        Set Predecessor = Nothing

    Else If node.HasLeftChild Then

        ' Это указатель на узел.

        ' Найти самый правый узел в левой ветви.

        Set child = node.LeftChild

        Do While child.HasRightChild

           Set child = child.RightChild

        Loop

        Set Predecessor = child

    Else

        ' Ссылка указывает на предшественника.

        Set Predecessor = node.LeftChild

    End If

End Function

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

Удобно также ввести функции для нахождения первого и последнего узлов дерева. Чтобы найти первый узел, просто проследуем по указателям на левого потомка вниз от корня до тех пор, пока не достигнем узла, значение указателя на левого потомка для которого равно Nothing. Чтобы найти последний узел, проследуем по указателям на правого потомка вниз от корня до тех пор, пока не достигнем узла, значение указателя на правого потомка для которого равно Nothing.

Private Function FirstNode() As ThreadedNode

Dim node As ThreadedNode

    Set node = Root

    Do While Not (node.LeftChild Is Nothing)

        Set node = node.LeftChild

    Loop

    Set PirstNode = node

End Function

Private Function LastNode() As ThreadedNode

Dim node As ThreadedNode

    Set node = Root

    Do While Not (node.RightChild Is Nothing)

        Set node = node.RightChild

    Loop

    Set FirstNode = node

End Function

=========142

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

Private Sub Inorder()

Dim node As ThreadedNode

    ' Найти первый узел.

    Set node = FirstNode()

    ' Вывод списка.

    Do While Not (node Is Nothing)

        Print node.Value

        Set node = Successor(node)

    Loop

End Sub

Private Sub PrintReverseInorder()

Dim node As ThreadedNode

    ' Найти последний узел

    Set node = LastNode

    ' Вывод списка.

    Do While Not (node Is Nothing)

        Print node. Value

        Set node = Predecessor(node)

    Loop

End Sub

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

Каждый указатель на дочерние узлы в дереве содержит или указатель на потомка, или ссылку на предшественника или последователя. Так как каждый узел имеет два указателя на дочерние узлы, то, если дерево имеет N узлов, то оно будет содержать 2 * N ссылок и указателей. Эти алгоритмы обхода обращаются ко всем ссылкам и указателям дерева один раз, поэтому они потребуют выполнения O(2 * N) = O(N) шагов.

Можно немного ускорить выполнение этих подпрограмм, если отслеживать указатели на первый и последний узлы дерева. Тогда вам не понадобится выполнять поиск первого и последнего узлов перед тем, как вывести список узлов по порядку. Так как при этом алгоритм обращается ко всем N узлам дерева, время выполнения этого алгоритма также будет порядка O(N), но на практике он будет выполняться немного быстрее.

========143

Работа с деревьями со ссылками

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

Предположим, что требуется добавить нового левого потомка узла A. Так как это место не занято, то на месте указателя на левого потомка узла A находится ссылка, которая указывает на предшественника узла A. Поскольку новый узел займет место левого потомка узла A, он станет предшественником узла A. Узел A будет последователем нового узла. Узел, который был предшественником узла A до этого, теперь становится предшественником нового узла. На рис. 6.23 показано дерево с рис. 6.22 после добавления нового узла X в качестве левого потомка узла H.

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

@Рис. 6.23. Добавление узла X к дереву со ссылками

=========144

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

Private Sub AddLeftChild(parent As ThreadedNode, child As ThreadedNode)

    ' Предшественник родителя становится предшественником нового узла.

    Set child. LeftChild = parent.LeftChild

    child.HasLeftChild = False

    ' Вставить узел.

    Set parent.LeftChild = child

    parent.HasLeftChild = True

    ' Родитель является последователем нового узла.

    Set child.RightChild = parent

    child.HasRightChild = False

    ' Определить, является ли новый узел первым узлом дерева.

    If child.LeftChild Is Nothing Then Set FirstNode = child

End Sub

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

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

Указатель на правого потомка удаляемого узла является ссылкой, которая указывает на следующий узел в дереве. Так как удаляемый узел является левым потомком своего родителя, и поскольку у него нет потомков, эта ссылка указывает на родителя, поэтому ее можно просто опустить. На рис. 6.24 показано дерево с рис. 6.23 после удаления узла F. Аналогично удаляется правый потомок.

Private Sub RemoveLeftChild(parent As ThreadedNode)

Dim target As ThreadedNode

    Set target = parent.LeftChild

    Set parent.LeftChild = target.LeftChild

End Sub

@Рис. 6.24. Удаление узла F из дерева со ссылками

=========145

Квадродеревья[RP12] 

Квадродеревья (quadtrees) описывают пространственные отношения между элементами на площади. Например, это может быть карта, а элементы могут представлять собой положение домов или предприятий на ней.

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

' Потомки.

Public NWchild As QtreeNode

Public NEchild As QtreeNode

Public SWchild As QtreeNode

Public SEchild As QtreeNode

' Элементы узла, если это не лист.

Public Items As New Collection

Элементы, записанные в квадродереве, могут содержать пространственные данные любого типа. Они могут содержать информацию о положении, которую дерево может использовать для поиска элементов. Переменные в простом классе QtreeItem, который представляет элементы, состоящие из точек на местности, определяются так:

Public X As Single

Public Y As Single

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

На рис. 6.25 показано несколько элементов данных, расположенных в виде квадродерева. Каждая область разбивается до тех пор, пока она не будет содержать не более двух элементов.

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

====146

@Рис. 6.25. Квадродерево

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

Функция LocateLeaf класса QtreeNode использует этот подход для поиска листа дерева, который содержит выбранную точку. Программа может вызвать эту функцию в строке Set the_leaf = Root.LocateLeaf(X, Y, Gxmin, Gxmax, Gymax), где Gxmin, Gxmax, Gymin, Gymax — это границы представленной деревом области.

Public Function LocateLeaf (X As Single, Y As Single, _

xmin As Single, xmax As Single, ymin As Single, ymax As Single) _

    As QtreeNode

Dim xmid As Single

Dim ymid As Single

Dim node As QtreeNode

    If NWchild Is Nothing Then

        ' Узел не имеет потомков. Искомый узел найден.

        Set LocateLeaf = Me

        Exit Function

    End If

    ' Найти соответстующего потомка.

    xmid = (xmax + xmin) / 2

    ymid = (ymax + ymin) / 2

    If X <= xmid Then

        If Y <= ymid Then

           Set LocateLeaf = NWchild.LocateLeaf( _

               X, Y, xmin, xmid, ymin, ymid)

        Else

           Set LocateLeaf = SWchild.LocateLeaf _

               X, Y, xmin, xmid, ymid, ymax)

        End If

    Else

        If Y <= ymid Then

           Set LocateLeaf = NEchild.LocateLeaf( _

               X, Y, xmid, xmax, ymin, ymid)

        Else

           Set LocateLeaf = SEchild.LocateLeaf( _

               X, Y, xmid, xmax, ymid, ymax)

        End If

    End If

End Function

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

Public Sub NearPointInLeaf (X As Single, Y As Single, _

best_item As QtreeItem, best_dist As Single, comparisons As Long)

Dim new_item As QtreeItem

Dim Dx As Single

Dim Dy As Single

Dim new_dist As Single

    ' Начнем с заведомо плохого решения.

    best_dist = 10000000

    Set best_item = Nothing

    ' Остановиться если лист не содержит элементов.

    If Items.Count < 1 Then Exit Sub

    For Each new_item In Items

        comparisons = comparisons + 1

        Dx = new_item.X - X

        Dy = new_item.Y - Y

        new_dist =Dx * Dx + Dy * Dy

        If best_dist > new_dist Then

           best_dist = new_dist

           Set best_item = new_item

        End If

    Next new_item

End Sub

======147-148

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

Предположим, что Dmin — это расстояние между выбранной пользователем точкой и ближайшим из найденных до сих пор населенных пунктов. Если Dmin меньше, чем расстояние от выбранной точки до края листа, то поиск закончен. Населенный пункт находится при этом слишком далеко от края листа, чтобы в каком‑либо другом листе мог существовать пункт, расположенный ближе к заданной точке.

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

Public Sub CheckNearbyLeaves(exclude As QtreeNode, _

    X As Single, Y As Single, best_item As QtreeItem, _

    best_dist As Single, comparisons As Long, _

    xmin As Single, xmax As Single, ymin As Single, ymax As Single)

Dim xmid As Single

Dim ymid As Single

Dim new_dist As Single

Dim new_item As QtreeItem

    ' Если это лист, который мы должны исключить,

    ' ничего не делать.

    If Me Is exclude Then Exit Sub

    ' Если это лист, проверить его.

    If SWchild Is Nothing Then

        NearPointInLeaf X, Y, new_item, new_dist, comparisons

        If best_dist > new_dist Then

           best_dist = new_dist

           Set best_item = new_item

        End If

        Exit Sub

    End If

    ' Найти потомков, которые удалены не больше, чем на best_dist

    ' от выбранной точки.

    xmid = (xmax + xmin) / 2

    ymid = (ymax + ymin) / 2

    If X - Sqr(best_dist) <= xmid Then

        ' Продолжаем с потомками на западе.

        If Y - Sqr(best_dist) <= ymid Then

           ' Проверить северо-западного потомка.

           NWchild.CheckNearbyLeaves _

               exclude, X, Y, best_item, _

               best_dist, comparisons, _

               xmin, xmid, ymin, ymid

        End If

        If Y + Sqr(best_dist) > ymid Then

           ' Проверить юго-западного потомка.

           SWchiId.CheckNearbyLeaves _

               exclude, X, Y, best_item, _

               best_dist, comparisons, _

               xmin, xmid, ymid, ymax

        End If

    End If

    If X + Sqr(best_dist) > xmid Then

        ' Продолжить с потомками на востоке.

        If Y - Sqr(best_dist) <= ymid Then

           ' Проверить северо-восточного потомка.

            NEchild.CheckNearbyLeaves _

               exclude, X, Y, best_item, _

               best_dist, comparisons, _

               xmid, xmax, ymin, ymid

        End If

        If Y + Sqr(best_dist) > ymid Then

           ' Проверить юговосточного потомка.

           SEchild.CheckNearbyLeaves _

               exclude, X, Y, best_item, _

               best_dist, comparisons, _

               xmid, xmax, ymid, ymax

        End If

    End If

End Sub

=====149-150

Подпрограмма FindPoint использует подпрограммы LocateLeaf, NearPointInLeaf, и CheckNearbyLeaves, из класса QtreeNode для быстрого поиска элемента в квадродереве.

Function FindPoint(X As Single, Y As Single, comparisons As Long) _ As QtreeItem

Dim leaf As QtreeNode

Dim best_item As QtreeItem

Dim best_dist As Single

    ' Определить, в каком листе находится точка.

    Set leaf = Root.LocateLeaf( _

        X, Y, Gxmin, Gxmax, Gymin, Gymax)

    ' Найти ближайшую точку в листе.

    leaf.NearPointInLeaf _

        X, Y, best_item, best_dist, comparisons

    ' Проверить соседние листья.

    Root.CheckNearbyLeaves _

        leaf, X, Y, best_item, best_dist, _

        comparisons, Gxmin, Gxmax, Gymin, Gymax

    Set FindPoint = best_item

End Function

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

Интересно наблюдать квадродеревья, элементы которых распределены неравномерно, поэтому программа выбирает точки при помощи функции странного аттрактора (strange attractor) из теории хаоса (chaos theory). Хотя кажется, что элементы следуют в случайном порядке, они образуют интересные кластеры.

При выборе какой‑либо точки на форме при помощи мыши, программа Qtree находит ближайший к ней элемент. Она подсвечивает этот элемент и выводит число проверенных при его поиске элементов.

В меню Options (Опции) программы можно задать, должна ли программа использовать квадродеревья или нет. Если поставить галочку в пункте Use Quadtree (Использовать квадродерево), то программа выводит на экран квадродерево и использует его для поиска элементов. Если этот пункт не выбран, программа не отображает квадродерево и находит нужные элементы путем перебора.

Программа проверяет намного меньшее число элементов и работает намного быстрее при использовании квадродерева. Если этот эффект не слишком заметен на вашем компьютере, запустите программу, задав при старте 10.000 или 20.000 входных элементов. Вы заметите разницу даже на компьютере с процессором Pentium с тактовой частотой 90 МГц.

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

Изменение MAX_PER_NODE

Интересно поэкспериментировать с программой Qtree, изменяя значение MAX_PER_NODE, определенное в разделе Declarations класса QtreeNode. Это максимальное число элементов, которые могут поместиться в узле квадродерева без его разбиения. Программа обычно использует значение MAX_PER_NODE = 100.

======151

@Рис. 6.26. Программа Qtree

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

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

Это пример компромисса между временем и пространством. Использование большего числа узлов квадродерева ускоряет поиск, но занимает больше памяти. В этом примере, при значении переменной MAX_PER_NODE примерно равном 100, достигается равновесие между скоростью и использованием памяти. Для других приложений вам может потребоваться поэкспериментировать с различными значениями переменной MAX_PER_NODE, чтобы найти оптимальное.

Использование псевдоуказателей в квадродеревьях

Программа Qtree использует большое число классов и коллекций. Каждый внутренний узел квадродерева содержит четыре ссылки на дочерние узлы. Листья включают большие коллекции, в которых находятся элементы узла. Все эти объекты и коллекции замедляют работу программы, если она содержит большое числе элементов. Создание объектов отнимает много времени и памяти. Если программа создает множество объектов, она может начать обращаться к файлу подкачки, что сильно замедлит ее работу.

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

=====152

Программа Qtree2 создает квадродерево при помощи псевдоуказателей. Узлы и элементы находятся в массивах определенных пользователем структур данных. В качестве указателей, эта программа использует индексы массивов вместо ссылок на объекты. В одном из тестов на компьютере с процессором Pentium с тактовой частотой 90 МГц, программе Qtree потребовалось 25 секунд для построения квадродерева, содержащего 30.000 элементов. Программе Qtree2 понадобилось всего 3 секунды для создания того же дерева.

Восьмеричные деревья

Восьмеричные деревья (octtrees) аналогичны квадродеревьям, но они разбивают область не двумерного, а трехмерного пространства. Восьмеричные деревья содержат не четыре потомка, как квадродеревья, а восемь, разбивая объем области на восемь частей — верхнюю северо‑западную, нижнюю северо‑западную, верхнюю северо‑восточную, нижнюю северо‑восточную и так далее.

Страницы: 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


на тему рефераты
НОВОСТИ на тему рефераты
на тему рефераты
ВХОД на тему рефераты
Логин:
Пароль:
регистрация
забыли пароль?

на тему рефераты    
на тему рефераты
ТЕГИ на тему рефераты

Рефераты бесплатно, реферат бесплатно, курсовые работы, реферат, доклады, рефераты, рефераты скачать, рефераты на тему, сочинения, курсовые, дипломы, научные работы и многое другое.


Copyright © 2012 г.
При использовании материалов - ссылка на сайт обязательна.