Скачать презентацию Динамические структуры данных Ранее рассмотренные типы переменных простые Скачать презентацию Динамические структуры данных Ранее рассмотренные типы переменных простые

lec06_03_Dinamicheskie_struktury_dannyh.ppt

  • Количество слайдов: 60

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

Область оперативной памяти для размещения динамических данных называется динамической памятью (Heap - куча, множество, Область оперативной памяти для размещения динамических данных называется динамической памятью (Heap - куча, множество, накопление) Тип-указатель Указатель дает адрес элемента определенного, базового типа - указывает на элемент базового типа. Стандартный тип-указатель Pointer является бестиповым и совместим с любым указателем. Объявление типа: type Pбазовый тип =^базовый тип; var p 1: Pбазовый тип; p 2: Pointer;

Операции, изменяющие значения указателей. • Операция взятия адреса с последующим присваиванием: указатель: =@переменная базового Операции, изменяющие значения указателей. • Операция взятия адреса с последующим присваиванием: указатель: =@переменная базового типа; • Операция присваивания для однотипных указателей. • Операция присваивания константы NIL любым указателям. NIL - пусто, ничто - переменная ни на что не указывает. Операции над указателями • Операции сравнения = <> для однотипных указателей.

 • Операция взятия элемента по указателю: указатель^ - значение элемента, с которым связан • Операция взятия элемента по указателю: указатель^ - значение элемента, с которым связан указатель. Примечание. Последняя операция иногда называется разыменованием. Исключения для операции разыменования. Объекты создаются только в динамической памяти. Соответствующие переменные фактически являются указателями на выделенные области. Но при работе с ними знак ^ не ставится. Пример: var Form 1: TForm; {указатель} …. . Form 1. Caption : =‘My. Form’; {якобы строка}

Стандартные процедуры для работы с динамической памятью procedure New(P); Процедура выделяет область памяти, соответствующую Стандартные процедуры для работы с динамической памятью procedure New(P); Процедура выделяет область памяти, соответствующую базовому типу указателя P. Переменная P принимает значение указателя на эту область. Можно использовать и как функцию: New(px); или px : = New(PByte); procedure Dispose(P); Процедура освобождает область памяти, на которую указывает P.

Переменная P становится неопределенной и ее нельзя применять до присваивания ей нового значения. procedure Переменная P становится неопределенной и ее нельзя применять до присваивания ей нового значения. procedure Get. Mem(P, Size); Процедура выделяет Size байт памяти. Переменная P принимает значение указателя на эту область. procedure Free. Mem(P, Size); Процедура освобождает область памяти, на которую указывает P. Параметр Size - необязателен, но если задается, то должен быть равен размеру области в байтах. Переменная P становится неопределенной и ее нельзя применять до присваивания ей нового значения.

При объявлении данных динамической структуры в разделе описаний указывается не сама переменная какого-либо типа, При объявлении данных динамической структуры в разделе описаний указывается не сама переменная какого-либо типа, а указатель (ссылка) на нее. В результате указатель будет обычной переменной, а переменная на которую он указывает – динамической. Var P: ^Char; Begin P^: =‘ж’; …. End; P: ^Char; адрес P^: Char; ‘ж’

Указательная переменная Р может быть в трех состояниях. 1. Содержать адрес какой-либо переменной, память Указательная переменная Р может быть в трех состояниях. 1. Содержать адрес какой-либо переменной, память под которую уже выделена. P P^ адрес 2. Содержать специальный пустой адрес nil. P nil 3. Находиться в неопределенном состоянии. P ? ?

В неопределенном состоянии указатель бывает в начале работы программы до первого присваивания ему или В неопределенном состоянии указатель бывает в начале работы программы до первого присваивания ему или конкретного адреса, или пустого адреса nil, a также после освобождения области памяти на которую он указывает. Различие между состоянием nil и неопределенным состоянием: P 1 nil P 2 nil P 1=P 2

P 1 ? P 2 ? ? ? P 1 <> P 2 Простейшие P 1 ? P 2 ? ? ? P 1 <> P 2 Простейшие действия с указателями Действие 1. Объявление type Pint=^integer; Var a, b: Pint; Результат a ? ? b ? ?

2. Выделение памяти a адрес1 New(a); New(b); b адрес2 a^ b^ 3. Занесение информации 2. Выделение памяти a адрес1 New(a); New(b); b адрес2 a^ b^ 3. Занесение информации a^: =1; a адрес1 b^: =2; b адрес2 1 2 4. Копирование информации a^ : =b^; a адрес1 1 2 a^ b адрес2 2 b^ a^=b^ a<>b

5. а)Копирование адреса a: =b; a адрес2 2 b адрес2 2 a^ b^ 2 5. а)Копирование адреса a: =b; a адрес2 2 b адрес2 2 a^ b^ 2 a^ 5 б) Dispose(a); a: =b; a адрес2 b адрес2 6. b : =nil; a адрес2 b nil b^ a^=b^ a=b

Динамические структуры Возможность динамического использования памяти позволяет создавать структуры данных с переменным числом элементов Динамические структуры Возможность динамического использования памяти позволяет создавать структуры данных с переменным числом элементов динамические структуры данных. Динамические массивы 1. Одномерные динамические массивы Динамические массивы используются в приложениях, где заранее неизвестно количество элементов и определяются в процессе выполнения программы. Синтаксис объявления динамического массива: Type <имя_типа> = array of <базовый тип> Var <имя_массива>: <имя_типа>;

Или Var <имя_массива>: array of <базовый_тип>; При объявлении динамического массива память для него не Или Var <имя_массива>: array of <базовый_тип>; При объявлении динамического массива память для него не выделяется. Прежде чем использовать массив, необходимо задать его размер. Процедура Set. Length задает количество элементов массива: Set. Length(<имя_массива>, <кол_во_элементов>); Например: Set. Length(a, 10); Индексы массива целые числа начинающиеся с нуля. Для данного примера элементы массива: a[0], a[1], …, a[9]

Повторное применение процедуры Set. Length к массиву изменяет его размер. Если значение больше предыдущего Повторное применение процедуры Set. Length к массиву изменяет его размер. Если значение больше предыдущего то значения массива сохраняются и в конце добавляются новые элементы, если наоборот то массив усекается до объявленного количества элементов. Пример: type mas=array of integer; Var a: mas; n, i: byte; Begin n: =5; Set. Length(a, n); { массив a(0, 0, 0)} for i: =0 to n-1 do a[i]: =i+1; { массив a(1, 2, 3, 4, 5)} n: =8;

Set. Length(a, n); { массив a(1, 2, 3, 4, 5, 0, 0, 0)} n: Set. Length(a, n); { массив a(1, 2, 3, 4, 5, 0, 0, 0)} n: =4; Setlength(a, n); { массив a(1, 2, 3, 4)} n: =5; Setlength(a, n); { массив a(1, 2, 3, 4, 0)} End; Усечение динамического массива лучше производить с помощью функции Copy: a: =Copy(a, 0, 3). Для массивов размещенных в памяти можно применять следующие функции: 1. Определение длины массива: Length( имя_массива) for i: =0 to length(a)-1 do оператор;

2. Определение верхней границы массива: High(имя_массива) for i: =0 to high(a) do оператор; Если 2. Определение верхней границы массива: High(имя_массива) for i: =0 to high(a) do оператор; Если массив имеет нулевую длину функция вернет значение – 1. 3. Определение нижней границы массива: Low(имя_массива) for i: =low(a) to length(a)-1 do оператор; Удаление из памяти динамического массива: 1. Присвоением массиву значения nil. 2. С помощью функции Finalize. 3. Установление нулевой длины функцией Set. Length.

Следующие операторы эквивалентны: 1. a: =nil; 2. Finalize(a); 3. Set. Length(a, 0); Пересвоение элементов Следующие операторы эквивалентны: 1. a: =nil; 2. Finalize(a); 3. Set. Length(a, 0); Пересвоение элементов массива В языке Паскаль строго соблюдается концепция типов. Поэтому принадлежность переменных к одному типу данных языка зависит от понятия эквивалентности типов. Она может быть структурной и именной. При структурной эквивалентности анализируется структура переменных: число их компонент и их типы.

Пример: type T 1 = Array [1. . 10] of Integer; T 2 = Пример: type T 1 = Array [1. . 10] of Integer; T 2 = Array [1. . 10] of Integer; var V 1: T 1; V 2: T 2; При структурной эквивалентности Т 1 и Т 2 – один тип. Но в языке паскаль принята именная эквивалентность. При этом типы переменных совпадают только в том случае, когда при их описании используется один и тот же идентификатор типа. Следовательно в языке паскаль Т 1 и Т 2 – 2 разных типа.

Типы Т 1 и Т 2 идентичны (одинаковые), если: • Т 1 и Т Типы Т 1 и Т 2 идентичны (одинаковые), если: • Т 1 и Т 2 один и тот же тип; • они определены один через другой. Т. о. если динамические массивы определены как переменные одного типа то пересвоение элементов массива возможно в следующих случаях: 1. Размер присваемого массива меньше чем размерность массива используемого в выражении. 2. Массив используемый в выражении равен nil. Пример: type mas = Array of Integer;

var V 1, V 2: mas; Begin …. . v 2: =nil; v 1: var V 1, V 2: mas; Begin …. . v 2: =nil; v 1: =v 2; …. . End; Пример неправильного пересвоения: var V 1: array of integer; V 2: array of integer; Begin …. .

v 1: =v 2; …. . End; Замечание: В операциях сравнения динамических массивов сравниваются v 1: =v 2; …. . End; Замечание: В операциях сравнения динамических массивов сравниваются только сами указатели, а не значения элементов массива, т. е. выражение а=b будет равно true только тогда когда указатели указывают на один и тот же массив. Выражение a[0]=b[0] сравнивает нулевые элементы массива.

Замечание: Процедуры New и Dispose с динамическими массивами не используются! Динамические массивы могут передаваться Замечание: Процедуры New и Dispose с динамическими массивами не используются! Динамические массивы могут передаваться в качестве параметров процедур и функций. Пример: Function Primer(a: array of string): boolean;

2. Многомерные динамические массивы Многомерный динамический массив определяется как динамический массив динамических массивов. Пример: 2. Многомерные динамические массивы Многомерный динамический массив определяется как динамический массив динамических массивов. Пример: type mas = array of integer; Для установления размерности динамического массива используется функция Set. Length: 1. При определении одинаковой размерности для строк и столбцов, необходимо передавать несколько параметров. Например: Set. Length(a, 4, 5); {задан массив размерностью 4 х5}

2. При задание массива разной размерности сначала определяется один диапазон, а затем размерности каждого 2. При задание массива разной размерности сначала определяется один диапазон, а затем размерности каждого массива. Например: Set. Length(a, 3); Set. Length(a[0], 4); Set. Length(a[1], 2); Set. Length(a[2], 3); Пример: Создать массив в котором число строк равно заданному числу N, а число столбцов в каждой строке равно номеру строки. type mas=array of byte; var a: mas; n, I, j, m: byte;

begin n: =3; m: =1; Set. Length(a, n); for i: =0 to n-1 do begin n: =3; m: =1; Set. Length(a, n); for i: =0 to n-1 do begin Set. Length(a[i], i+1); for j: =0 to i do begin a[i, j]: =m; inc(m) end end;

СВЯЗАННЫЕ ДИНАМИЧЕСКИЕ ДАННЫЕ Линейные списки - это данные динамической структуры, которые представляют собой совокупность СВЯЗАННЫЕ ДИНАМИЧЕСКИЕ ДАННЫЕ Линейные списки - это данные динамической структуры, которые представляют собой совокупность линейно связанных однородных элементов и для которых разрешены следующие действия: • добавление элемента в начало (голову) списка; • добавление элемента в конец (хвост) списка; • вставка элемента между двумя любыми другими элементами списка; • удаление любого, как крайнего, так и среднего, элемента списка.

Кольцевые списки — это такие же данные, как и линейные списки, но имеющие дополнительную Кольцевые списки — это такие же данные, как и линейные списки, но имеющие дополнительную связь между последним и первым элементами списка. Очередь — частный случай линейного односвязного списка, для которого разрешены только два действия: добавление элемента в конец (хвост) очереди и удаление элемента из начала (головы) очереди. Стек — частный случай линейного односвязного списка, для которого разрешено добавлять или удалять элементы только с одного конца списка, который называется вершиной (головой) стека. Деревья — это динамические данные иерархической структуры произвольной конфигурации. Элементы дерева называются вершинами (узлами).

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

Соответствующие ей объявления будут иметь такой вид: Type TPtr = ^ TElem; TElem = Соответствующие ей объявления будут иметь такой вид: Type TPtr = ^ TElem; TElem = record Inf : Real; Link : TPtr end;

Работа с очередью Для создания очереди и работы с ней необходимо иметь как минимум Работа с очередью Для создания очереди и работы с ней необходимо иметь как минимум два указателя: • на начало очереди (идентификатор Beg. Q), • на конец очереди (идентификатор End. Q) Кроме того, для освобождения памяти удаляемых элементов требуется дополнительный временный указатель (идентификатор Р). Создание очереди 1. Исходное состояние: Beg. Q End. Q P Beg. Q=nil nil ? End. Q=nil ?

2. Выделение памяти под первый элемент P очереди: Beg. Q End. Q nil New(P); 2. Выделение памяти под первый элемент P очереди: Beg. Q End. Q nil New(P); nil ? ? Inf Link ? 3. Занесение информации в первый элемент очереди: P Beg. Q End. Q nil P^. inf: =3; Inf Link 3 nil P^. link: =nil;

4. Установка указателей Beg. Q созданный первый элемент: Beg. Q End. Q P и 4. Установка указателей Beg. Q созданный первый элемент: Beg. Q End. Q P и End. Q Beg. Q: =P; End. Q: =P; Inf Link 3 nil Добавление элемента очереди 1. Исходное состояние: Beg. Q 2 End. Q 4 P 3 nil ? на

2. Выделение памяти под новый элемент и занесение в него информации: Beg. Q 2 2. Выделение памяти под новый элемент и занесение в него информации: Beg. Q 2 4 End. Q P 3 nil 5 nil New(P); P^. inf: =5; P^. link: =nil; 3. Установка связи между последним элементом очереди и новым, а также перемещение указателя конца очереди End. Q на новый элемент: Beg. Q 2 End. Q 4 3 P 5 nil End. Q^. link: =P; End. Q: =P;

Удаление элемента очереди 1. Исходное состояние: Beg. Q P End. Q ? 2 4 Удаление элемента очереди 1. Исходное состояние: Beg. Q P End. Q ? 2 4 3 5 nil 2. Извлечение информации из удаляемого элемента в переменную Val и установка на него вспомогательного указателя Р: Val: =Beg. Q^. inf; Beg. Q Val 2 P 2 4 P: =Beg. Q; 3 End. Q 5 nil

3. Перестановка указателя начала очереди Beg. Q на следующий элемент, используя значение поля Link, 3. Перестановка указателя начала очереди Beg. Q на следующий элемент, используя значение поля Link, которое хранится в первом элементе. После этого освобождается память начального элемента очереди, используя дополнительный указатель Р: Beg. Q P End. Q Val 2 2 4 3 Beg. Q : =P^. Link; Dispose(P); 5 nil

Пример: Создания и удаления очереди. 1) процедура Add, которая в зависимости от состояния очереди Пример: Создания и удаления очереди. 1) процедура Add, которая в зависимости от состояния очереди создает первый или добавляет очередной элемент в конец очереди; 2) процедура Del, которая извлекает информацию из начального элемента очереди с последующим освобождением его памяти. type TPtr = ^ TElem; TElem = record Inf : Real; Link : TPtr end; var Beg. Q, End. Q : TPtr; Value : Real;

procedure Add (Val : Real ); var P : TPtr; begin New(P); P^. Inf procedure Add (Val : Real ); var P : TPtr; begin New(P); P^. Inf : = Val; P^. Link : = nil; { если создается первый элемент очереди } if End. Q = nil then Beg. Q : = P { если создается очередной элемент очереди } else End. Q^. Link : = P; End. Q : = P; end;

procedure Del ( var Val : Real ); var P : TPtr; begin Val procedure Del ( var Val : Real ); var P : TPtr; begin Val : = Beg. Q^. Inf; P : = Beg. Q; Beg. Q : = P^. Link; { если удаляется последний элемент очереди } if Beg. Q = nil then End. Q : = nil; Dispose (P); end;

{ Начальные установки указателей } Beg. Q : = nil; End. Q : = { Начальные установки указателей } Beg. Q : = nil; End. Q : = nil; { Создание очереди из 10 -ти элементов } for i : =1 to 10 do Add (i); { Удаление очереди } while Beg. Q <> nil do Del(Value);

Работа со стеком Для работы со стеком, в отличие от очереди, необходимо иметь один Работа со стеком Для работы со стеком, в отличие от очереди, необходимо иметь один основной указатель на вершину стека (идентификатор Тор) и один дополнительный временный указатель (идентификатор Р), который используется для выделения и освобождения памяти элементов стека. Создание стека 1. Исходное состояние: P nil Top: =nil; ? ?

2. Выделение памяти под первый элемент стека и внесение в него информации: Top P 2. Выделение памяти под первый элемент стека и внесение в него информации: Top P nil 5 nil New(P); P^. inf: =5; P^. Link: =nil; 3. Установка вершины стека Тор на созданный элемент: Top P 5 nil Top: =P;

Добавление элемента в стек 1. Исходное состояние: P Top ? ? 3 1 5 Добавление элемента в стек 1. Исходное состояние: P Top ? ? 3 1 5 nil

2. Выделение памяти под новый элемент стека: P Top ? ? 3 1 5 2. Выделение памяти под новый элемент стека: P Top ? ? 3 1 5 nil New(P); ?

3. Внесение значения в информационное поле нового элемента и установка связи между ним и 3. Внесение значения в информационное поле нового элемента и установка связи между ним и "старой" вершиной стека Тор: P 10 P^. inf: =Val; P^. Link: =Top; Top 3 1 5 nil

4. Перемещение вершины стека Тор на новый элемент: Top: =P; P Top 10 3 4. Перемещение вершины стека Тор на новый элемент: Top: =P; P Top 10 3 1 5 nil

Удаление элемента стека 1. Исходное состояние: Top Val ? 10 P ? ? 3 Удаление элемента стека 1. Исходное состояние: Top Val ? 10 P ? ? 3 1 5 nil

2. Извлечение информации из информационного поля вершины стека Тор в переменную Val и установка 2. Извлечение информации из информационного поля вершины стека Тор в переменную Val и установка на вершину стека вспомогательного указателя Р: Val 10 Top Val: =Top^. inf; P: =Top; 10 3 1 5 nil P

3. Перемещение указателя вершины стека Тор на следующий элемент и освобождение памяти, занимаемой 3. Перемещение указателя вершины стека Тор на следующий элемент и освобождение памяти, занимаемой "старой" вершиной стека: Val 10 P 10 Top 3 Top: =P^. Link; Dispose(P); 1 5 nil

Работа со списками Универсальный список TList Delphi предоставляет программистам стандартный тип TList: список с Работа со списками Универсальный список TList Delphi предоставляет программистам стандартный тип TList: список с элементами (свойство Items) типа Pointer - универсальными указателями и методами, решающими все задачи со списками. Универсальность типа элементов списка позволяет формировать его из произвольных объектов. Методы TList · function Add(Item: Pointer): Integer; Вызов функции обеспечивает вставку нового элемента Item в конец списка. Функция возвращает номер нового элемента списка (нумерация с 0).

· procedure Insert(Index: Integer; Item: Pointer); Процедура обеспечивает вставку элемента Item в список в · procedure Insert(Index: Integer; Item: Pointer); Процедура обеспечивает вставку элемента Item в список в позицию, указанную параметром Index. · procedure Delete(Index: Integer); Процедура удаляет элемент с номером Index из списка. · procedure Clear; Процедура удаляет все элементы из списка, устанавливает свойство Count=0, освобождает память. · procedure Move(Old. Index, New. Index: Integer); Процедура переставляет элемент с места Old. Index на новое место New. Index.

Пример использования TList При нажатии на кнопку формируется список с элементами - указателями на Пример использования TList При нажатии на кнопку формируется список с элементами - указателями на запись. Разыменованные элементы преобразуются в строки, отображаемые в компоненте TList. Box. procedure TForm 1. Button 1 Click(Sender: TObject); type PAList = ^AList; AList = record I: Integer; C: string[5]; end; var My. List: TList; ARecord: PAList; B: Byte;

begin My. List : = TList. Create; New(ARecord); ARecord^. I : = 1; ARecord^. begin My. List : = TList. Create; New(ARecord); ARecord^. I : = 1; ARecord^. C : = ’ item'; My. List. Add(ARecord); {добавили 1 и ‘item’ в список} New(ARecord); ARecord^. I : = 2; ARecord^. C : = ’ item'; My. List. Add(ARecord);

for B : = 0 to My. List. Count - 1 do begin ARecord for B : = 0 to My. List. Count - 1 do begin ARecord : = My. List. Items[B]; List. Box 1. Items. Add(Int. To. Str(ARecord^. I)+ ARecord^. C); end; { очистка памяти от элементов списка } for B : = 0 to My. List. Count - 1 do begin ARecord : = My. List. Items[B]; Dispose(ARecord); end; { очистка памяти от списка } My. List. Free; end;

Универсальное дерево TTree. View Delphi предоставляет программистам стандартный тип TTree. View: Универсальное (не бинарное!)дерево Универсальное дерево TTree. View Delphi предоставляет программистам стандартный тип TTree. View: Универсальное (не бинарное!)дерево с элементами (свойство Items типа TTree. Nodes массив узлов дерева). Узел (TTree. View. Items[num]) имеет тип TTree. Node. Универсальность типа данных в узле (свойство Data типа Pointer у TTree. Node) позволяет формировать дерево из произвольных объектов. (Tree - дерево, Node - узел, Data - данные)

Для отображения дерева используются следующие свойства: TTree. View: Images - набор изображений для узлов; Для отображения дерева используются следующие свойства: TTree. View: Images - набор изображений для узлов; TTree. Node: Text - текст, надписываемый в узлах. Дерево можно построить на этапе проектирования в специальном редакторе или при выполнении приложения.

Стандартные методы для деревьев. Добавление узла: Add, Add. Child. First, Add. First (для отображения Стандартные методы для деревьев. Добавление узла: Add, Add. Child. First, Add. First (для отображения в форме) Add. Child. Object, Add. Child. Object. First, Add. Object. First (с данными - для формирования структуры данных) Child - дочерний узел. Очистка дерева - Clear Создание дерева – Create Удаление указанного узла - Delete

Удаление дерева - Destroy Выбор корня - Get. First. Node Выбор узла - Get. Удаление дерева - Destroy Выбор корня - Get. First. Node Выбор узла - Get. Node Вставка узла - Insert Вставка узла с данными – Insert. Object Пример. В приложении при нажатии на кнопку происходит построение дерева. (Форма содержит компоненты TButton, TImage. List, Ttree. View)

procedure TForm 1. Button 1 Click(Sender: TObject); Var My. Tree. Node 1, My. Tree. procedure TForm 1. Button 1 Click(Sender: TObject); Var My. Tree. Node 1, My. Tree. Node 2: TTree. Node; begin with Tree. View 1. Items do begin Clear; { удалены все имеющиеся узлы } My. Tree. Node 1 : = Add(nil, 'Root. Tree. Node 1'); {добавлен корень } Add. Child(My. Tree. Node 1, 'Child. Node 1'); { добавлено поддерево } My. Tree. Node 2 : = Add(My. Tree. Node 1, 'Root. Tree. Node 2'); {второй корень}

Add. Child(My. Tree. Node 2, 'Child. Node 2'); {добавлено поддерево } {переменная My. Tree. Add. Child(My. Tree. Node 2, 'Child. Node 2'); {добавлено поддерево } {переменная My. Tree. Node 2 используется для добавления следующего поддерева } My. Tree. Node 2 : = Tree. View 1. Items[3]; Add. Child(My. Tree. Node 2, 'Child. Node 2 a'); {поддерево после Child. Node 2 a } Add(My. Tree. Node 2, 'Child. Node 2 b'); {еще один корень: } Add(My. Tree. Node 1, 'Root. Tree. Node 3'); end;