Для листинга кода определения класса см. dlnode Резюме Класса.
Чтобы использовать класс, создайте папку под названием @dlnode
и сохраните dlnode.m
в эту папку. Родительская папка @dlnode
должна быть на пути MATLAB®. Также сохраните dlnode.m
в папку path.
дизайн Класса dlnode
dlnode
является классом для создания двунаправленных связанных списков, в которых каждый узел содержит:
Массив данных
Обработайте к следующему узлу
Обработайте к предыдущему узлу
Каждый узел имеет методы, которые позволяют узлу быть:
Вставленный перед заданным узлом в связанный список
Вставленный после определенного узла в связанный список
Удаленный из списка
Класс dlnode
реализует каждый узел как объект указателя с тремя свойствами:
Данные
Содержит данные для этого узла
Next
— Содержит указатель следующего узла в списке (SetAccess = private
)
Prev
— Содержит указатель предыдущего узла в списке (SetAccess = private
)
Эта схема показывает список с n1
с тремя узлами, n2
и n3
. Это также показывает, как узлы ссылаются на следующие и предыдущие узлы.
Класс dlnode
реализует следующие методы:
dlnode
— Создайте узел и присвойте значение, переданное как входной параметр свойству Data
insertAfter
Вставьте этот узел после заданного узла
insertBefore
Вставьте этот узел перед заданным узлом
removeNode
— Удалите этот узел из списка и повторно подключите остающиеся узлы
clearList
— Удалите большие списки эффективно
удаление
Закрытый метод, вызванный MATLAB при удалении списка.
Создайте узел путем передачи данных узла конструктору класса dlnode
. Например, эти операторы создают три узла со значениями данных 1
, 2
и 3
:
n1 = dlnode(1); n2 = dlnode(2); n3 = dlnode(3);
Встройте эти узлы в двунаправленный связанный список с помощью методов класса, разработанных с этой целью:
n2.insertAfter(n1) % Insert n2 after n1 n3.insertAfter(n2) % Insert n3 after n2
Теперь эти три узла соединяются:
n1.Next % Points to n2
ans = dlnode with properties: Data: 2 Next: [1x1 dlnode] Prev: [1x1 dlnode]
n2.Next.Prev % Points back to n2
ans = dlnode with properties: Data: 2 Next: [1x1 dlnode] Prev: [1x1 dlnode]
n1.Next.Next % Points to n3
ans = dlnode with properties: Data: 3 Next: [] Prev: [1x1 dlnode]
n3.Prev.Prev % Points to n1
ans = dlnode with properties: Data: 1 Next: [1x1 dlnode] Prev: []
Каждый узел уникален в этом, никакие два узла не могут быть до или рядом с тем же узлом.
Например, объект узла, node
, содержит в его свойстве Next
указатель следующего объекта узла, node.Next
. Точно так же свойство Prev
содержит указатель предыдущего узла, node.Prev
. Используя связанный список с тремя узлами, заданный в предыдущем разделе, можно продемонстрировать, что следующие операторы верны:
n1.Next == n2 n2.Prev == n1
Теперь предположите, что вы присваиваете n2
x
:
x = n2;
Следующие два равенства затем верны:
x == n1.Next x.Prev == n1
Но каждый экземпляр узла уникален, таким образом, существует только один узел в списке, который может удовлетворить условия того, чтобы быть равным n1.Next
и наличию свойства Prev
, которое содержит указатель на n1
. Поэтому x
должен указать на тот же узел как n2
.
Должен быть путь к нескольким переменным, чтобы относиться к тому же объекту. Класс handle
MATLAB обеспечивает средние значения и для x
и для n2
, чтобы относиться к тому же узлу.
Класс Handle задает метод eq
(используйте methods('handle')
, чтобы перечислить методы класса Handle), который включает использование оператора ==
со всеми объектами указателя.
Для получения дополнительной информации о классах Handle смотрите Сравнение Классов Указателя и Значения.
резюме Класса dlnode
В этом разделе описываются реализацию класса dlnode
.
Пример кода | Обсуждение |
---|---|
classdef dlnode < handle
| |
properties Data end | дизайн Класса dlnode |
properties (SetAccess = private) Next = dlnode.empty Prev = dlnode.empty end | Атрибуты свойств: Инициализируйте эти свойства к Для получения общей информации о свойствах, см. Синтаксис свойств |
methods
| Для получения общей информации о методах, seeMethods в Дизайне Класса |
function node = dlnode(Data) if (nargin > 0) node.Data = Data; end end | Создание отдельного узла (не соединенный) требует только данных. Для получения общей информации о конструкторах, см. Инструкции для Конструкторов |
function insertAfter(newNode, nodeBefore) removeNode(newNode); newNode.Next = nodeBefore.Next; newNode.Prev = nodeBefore; if ~isempty(nodeBefore.Next) nodeBefore.Next.Prev = newNode; end nodeBefore.Next = newNode; end | Вставьте узел в двунаправленный связанный список после заданного узла или соедините два заданных узла, если уже нет списка. Присваивает правильные значения для свойств |
function insertBefore(newNode, nodeAfter) removeNode(newNode); newNode.Next = nodeAfter; newNode.Prev = nodeAfter.Prev; if ~isempty(nodeAfter.Prev) nodeAfter.Prev.Next = newNode; end nodeAfter.Prev = newNode; end | Вставьте узел в двунаправленный связанный список перед заданным узлом или соедините два заданных узла, если уже нет списка. Этот метод присваивает правильные значения для свойств Смотрите вставляют узлы |
function removeNode(node) if ~isscalar(node) error('Nodes must be scalar') end prevNode = node.Prev; nextNode = node.Next; if ~isempty(prevNode) prevNode.Next = nextNode; end if ~isempty(nextNode) nextNode.Prev = prevNode; end node.Next = = dlnode.empty; node.Prev = = dlnode.empty; end | Удалите узел и зафиксируйте список так, чтобы остающиеся узлы были правильно соединены. аргумент Однажды нет никаких ссылок на узел, MATLAB удаляет его. |
function clearList(node) prev = node.Prev; next = node.Next; removeNode(node) while ~isempty(next) node = next; next = node.Next; removeNode(node); end while ~isempty(prev) node = prev; prev = node.Prev; removeNode(node) end end | Избегайте рекурсивных вызовов деструктора в результате очистки переменной списка. Цикл через список, чтобы отключить каждый узел. Когда нет никаких ссылок на узел, MATLAB вызывает деструктор класса (см. метод |
methods (Access = private) function delete(node) clearList(node) end | Метод деструктора класса. MATLAB вызывает метод |
end end | Конец закрытых методов и конец определения класса. |
Только методы класса dlnode
могут установить свойства Next
и Prev
, потому что эти свойства имеют частный доступ к набору (SetAccess = private
). Используя частный набор доступ препятствует тому, чтобы клиентский код выполнил любую неправильную операцию с этими свойствами. Методы класса dlnode
выполняют все операции, которые позволены на этих узлах.
Свойство Data
имеет общедоступный набор, и получите доступ, позволив вам запросить и изменить значение Data
как требуется.
Вот то, как класс dlnode
задает свойства:
properties Data end properties(SetAccess = private) Next = dlnode.empty; Prev = dlnode.empty; end
Чтобы создать объект узла, задайте данные узла в качестве аргумента конструктору:
function node = dlnode(Data) if nargin > 0 node.Data = Data; end end
Существует два метода для вставки узлов в список — insertAfter
и insertBefore
. Эти методы выполняют подобные операции, таким образом, этот раздел описывает только insertAfter
подробно.
function insertAfter(newNode, nodeBefore) removeNode(newNode); newNode.Next = nodeBefore.Next; newNode.Prev = nodeBefore; if ~isempty(nodeBefore.Next) nodeBefore.Next.Prev = newNode; end nodeBefore.Next = newNode; end
Как работы insertAfter
. Во-первых, insertAfter
вызывает метод removeNode
, чтобы гарантировать, что новый узел не соединяется ни с какими другими узлами. Затем insertAfter
присваивает свойства newNode
Next
и Prev
указателям узлов, которые являются после и перед местоположением newNode
в списке.
Например, предположите, что вы хотите вставить новый узел, nnew
, после существующего узла, n1
, в списке, содержащем n1—n2—n3
.
Во-первых, создайте nnew
:
nnew = dlnode(rand(3));
Затем, вызовите insertAfter
, чтобы вставить nnew
в список после n1
:
nnew.insertAfter(n1)
Метод insertAfter
выполняет следующие шаги, чтобы вставить nnew
в список между n1
и n2
:
Установите nnew.Next
на n1.Next
(n1.Next
является n2
):
nnew.Next = n1.Next;
Установите nnew.Prev
на n1
nnew.Prev = n1;
Если n1.Next
не пуст, то n1.Next
является все еще n2
, таким образом, n1.Next.Prev
является n2.Prev
, который установлен в nnew
n1.Next.Prev = nnew;
n1.Next
теперь установлен to nnew
n1.Next = nnew;
Метод removeNode
удаляет узел из списка и повторно подключает остающиеся узлы. insertBefore
и методы insertAfter
всегда вызывают removeNode
на узле, чтобы вставить прежде, чем попытаться соединить его со связанным списком.
Вызов removeNode
гарантирует, что узел находится в известном состоянии прежде, чем присвоить его свойству Next
или Prev
:
function removeNode(node) if ~isscalar(node) error('Input must be scalar') end prevNode = node.Prev; nextNode = node.Next; if ~isempty(prevNode) prevNode.Next = nextNode; end if ~isempty(nextNode) nextNode.Prev = prevNode; end node.Next = dlnode.empty; node.Prev = dlnode.empty; end
Например, предположите, что вы удаляете n2
из списка с тремя узлами (n1–n2–n3
):
n2.removeNode;
removeNode
удаляет n2
из списка и повторно подключает остающиеся узлы со следующими шагами:
n1 = n2.Prev; n3 = n2.Next; if n1 exists, then n1.Next = n3; if n3 exists, then n3.Prev = n1
Список воссоединен, потому что n1
соединяется с n3
и подключениями n3
к n1
. Последний шаг должен гарантировать, что n2.Next
и n2.Prev
оба пусты (то есть, n2
не соединяется):
n2.Next = dlnode.empty; n2.Prev = dlnode.empty;
Предположим, что вы создаете список с 10 узлами и сохраняете указатель на заголовок списка:
head = dlnode(1); for i = 10:-1:2 new = dlnode(i); insertAfter(new,head); end
Теперь удалите третий узел (свойство Data
присвоило значение 3
):
removeNode(head.Next.Next)
Теперь третий узел в списке имеет значение данных 4
:
head.Next.Next
ans = dlnode with properties: Data: 4 Next: [1x1 dlnode] Prev: [1x1 dlnode]
И предыдущий узел имеет значение Data
2
:
head.Next
ans = dlnode with properties: Data: 2 Next: [1x1 dlnode] Prev: [1x1 dlnode]
Чтобы удалить узел, вызовите метод removeNode
на том узле. Метод removeNode
отключает узел и повторно подключает список прежде, чем позволить MATLAB уничтожать удаленный узел. MATLAB уничтожает узел, если ссылки на него другими узлами удалены, и список повторно подключен.
Когда вы создаете связанный список и присваиваете переменную, которая содержит, например, голова или хвост списка, очищая ту переменную заставляют деструктор, рекурсивно вызывают через целый список. С достаточно большим списком, очищая переменную списка может привести к MATLAB, превышающему его предел рекурсии.
Метод clearList
избегает рекурсии и улучшает производительность удаления больших списков цикличным выполнением по списку и разъединением каждого узла. clearList
принимает указатель любого узла в списке и удаляет остающиеся узлы.
function clearList(node) if ~isscalar(node) error('Input must be scalar') end prev = node.Prev; next = node.Next; removeNode(node) while ~isempty(next) node = next; next = node.Next; removeNode(node); end while ~isempty(prev) node = prev; prev = node.Prev; removeNode(node) end end
Например, предположите, что вы создаете список со многими узлами:
head = dlnode(1); for k = 100000:-1:2 nextNode = dlnode(k); insertAfter(nextNode,head) end
Переменный head
содержит указатель на узел во главе списка:
head
head = dlnode with properties: Data: 1 Next: [1x1 dlnode] Prev: []
head.Next
ans = dlnode with properties: Data: 2 Next: [1x1 dlnode] Prev: [1x1 dlnode]
Можно вызвать clearList
, чтобы удалить целый список:
clearList(head)
Единственные узлы, которые не были удалены MATLAB, являются теми узлами, для которых там существует прямая ссылка. В этом случае теми ссылками является head
и nextNode
:
head
head = dlnode with properties: Data: 1 Next: [] Prev: []
nextNode
nextNode = dlnode with properties: Data: 2 Next: [] Prev: []
Можно удалить эти узлы путем очистки переменных:
clear head nextNode
Метод delete
просто вызывает метод clearList
:
methods (Access = private) function delete(node) clearList(node) end end
Метод delete
имеет частный доступ, чтобы препятствовать тому, чтобы пользователи вызвали delete
при намерении удалить единственный узел. MATLAB вызывает delete
неявно, когда список уничтожается.
Чтобы удалить единственный узел из списка, используйте метод removeNode
.
Специализация dlnode Класса
Класс dlnode
реализует двунаправленный связанный список и обеспечивает удобную отправную точку для создания более специализированных типов связанных списков. Например, предположите, что вы хотите создать список, в котором каждый узел имеет имя.
Вместо того, чтобы копировать код раньше реализовывал класс dlnode
, и затем подробно останавливающийся на нем, можно вывести новый класс от dlnode
(то есть, разделить на подклассы dlnode
). Можно создать класс, который имеет все функции dlnode
и также задает его собственные дополнительные функции. И потому что dlnode
является классом Handle, этот новый класс является классом Handle также.
Определение класса NamedNode
Чтобы использовать класс, создайте папку под названием @NamedNode
и сохраните NamedNode.m
в эту папку. Родительская папка @NamedNode
должна быть на пути MATLAB. Также сохраните NamedNode.m
в папку path.
Следующее определение класса показывает, как вывести класс NamedNode
от класса dlnode
:
classdef NamedNode < dlnode properties Name = '' end methods function n = NamedNode (name,data) if nargin == 0 name = ''; data = []; end n = n@dlnode(data); n.Name = name; end end end
Класс NamedNode
добавляет свойство Name
сохранить имя узла.
Конструктор вызывает конструктора класса для класса dlnode
, и затем присваивает значение свойству Name
.
Использование NamedNode, чтобы создать двунаправленный связанный список
Используйте класс NamedNode
как класс dlnode
, за исключением того, что вы задаете имя для каждого объекта узла. Например:
n(1) = NamedNode('First Node',100); n(2) = NamedNode('Second Node',200); n(3) = NamedNode('Third Node',300);
Теперь используйте методы вставки, наследованные от dlnode
, чтобы создать список:
n(2).insertAfter(n(1)) n(3).insertAfter(n(2))
Единственный узел отображает свое имя и данные, когда вы запрашиваете его свойства:
n(1).Next
ans = NamedNode with properties: Name: 'Second Node' Data: 200 Next: [1x1 NamedNode] Prev: [1x1 NamedNode]
n(1).Next.Next
ans = NamedNode with properties: Name: 'Third Node' Data: 300 Next: [] Prev: [1x1 NamedNode]
n(3).Prev.Prev
ans = NamedNode with properties: Name: 'First Node' Data: 100 Next: [1x1 NamedNode] Prev: []