Основная проблема заключается в том, что вам нужно заменить указатели или ссылки из представления в памяти чем-то другим, что может быть использовано для однозначного представления узла, на который был указан.
foo
/ \
cat zebra
\
dog
В одну сторонусделать это - заменить указатели на ключи - больше похоже на индекс массива, чем на правильный указатель.
1 2 "foo"
3 _ "cat"
_ _ "zebra"
_ _ "dog"
В этом представлении первое поле - это номер строки (отсчет начинается с 0, то естькорневой узел) левого потомка, второе поле - это правый потомок, а третье поле - это значение.Дерево упорядочено по алфавиту.Это кажется простым, но на самом деле может быть трудным сделать.
Подобный подход поместил бы ключ в каждую запись, а не полагался на позицию.Этот метод мог бы использовать исходные указатели в качестве ключей, а код для чтения должен был бы создать таблицу перевода / символов для переключения между ключами и новыми указателями.
Еще один способ сделать это с помощью lisp-esque tree: (foo (cat () (dog () ()) (zebra () ()))
Отформатировано для удобного просмотра:
(foo
(cat
()
(dog
()
()
)
)
(zebra
()
()
)
)
Это может быть легко сгенерированос помощью простого обхода в порядке. Он также может быть прочитан с помощью очень простого рекурсивного приличного синтаксического анализатора. Вы также можете изменить это, чтобы уменьшить размеры листовых узлов в сериализованном формате, пропустив nil
или ()
или что-либо другоевыбрал для указателей NULL.
Другой метод, аналогичный первому, заключается в хранении всего дерева в одном куске памяти, который может быть записан на диск и считан с диска. Указатели в этом случае будут относительнымив начале этого фрагмента памяти, а не абсолютных указателей. Это был бы быстрый способ для двух программ на одном и том же типе машины (использующих одинаковую ширину памяти ЦП)Рис (или другие графики), но, вероятно, будет трудно реализовать.
Версия lisp-esqe очень проста в реализации, но не распространяется на вещи, которые не являются деревьями, где естьможет быть циклическая ссылка или более одного родителя для конкретного узла, хотя это может быть сделано.Кроме того, его нелегко расширить для обработки хранения более одной структуры в определенном файле.
Версия позиционного индекса работает для большинства типов графиков, но при сохранении более одной структуры в конкретном файле потребуется изменитьэтот формат несколько.
Независимо от того, что вы выбираете, вам нужно будет убедиться, что вы можете обрабатывать все значения, которые могут быть представлены в качестве данных узла.Например, если данные узла могут содержать "
, )
или \n
, то это может вызвать проблемы в некоторых форматах, которые я показываю, и эти символы должны быть экранированы.Для этого вы можете добавить префикс полей к их длине или использовать постоянную структуру структуры.
Вам также необходимо убедиться, что любые двоичные поля хранятся в порядке байтов, если вы планируете обмениваться данными междуразные типы машин.Вам также нужно, чтобы эти данные имели одинаковый размер (используйте типы stdint.h, а не int и long) и имели каноническое представление для таких вещей, как числа с плавающей запятой.