Списки общего вида. Примеры: ортосписки

Модуль "Линейные списки" • урок 6 из 9 • ~7 минут

Суть урока. Списки общего вида - это обобщение, при котором элементом списка может быть не только данное (атом), но и другой список (подсписок). Это позволяет строить иерархии любой глубины. Яркий пример нелинейного списка - ортосписок для эффективного хранения разреженных матрицы.
Списки общего вида. Примеры: два узла помечены флагом ATOM и хранят числа, а средний помечен SUBLIST и указывает на вложенную цепочку
Средний узел вместо числа хранит ссылку на вложенный подсписок

Списки общего вида. Примеры: что это такое

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

К таким структурам относятся ортосписки (ортогональные списки) и суперсписки.

Разреженные матрицы (Ортосписки)

Одной из практически значимых разновидностей являются разреженные матрицы. Это огромные двумерные таблицы, где подавляющее большинство элементов равны нулю. Хранить их в виде обычного двумерного массива (например, int A[10000][10000]) крайне неэффективно по памяти.

Вместо этого используется структура ортосписка:

Получается "ортогональная" сеть указателей, которая хранит только полезные (ненулевые) данные, игнорируя пустоты.

А как в Python? В Python списки по своей природе уже являются списками общего вида. Когда вы пишете L = [1, [2, 3], 4], интерпретатор берет на себя всю работу с типами и ссылками. В C строгая типизация не позволяет так просто смешивать числа и подсписки в одном массиве.

Реализация на C (Объединения)

Так как элемент списка общего вида может быть либо значением (атомом), либо указателем на вложенный подсписок, для реализации такого узла на C обычно применяется объединение (union) вместе с флагом типа:

typedef enum { ATOM, SUBLIST } NodeType;

typedef struct ListNode {
    NodeType type;            /* Флаг: что хранится в данном узле? */
    union {
        int data;             /* Если это ATOM, храним данные */
        struct ListNode* sub; /* Если SUBLIST - указатель на подсписок */
    } value;
    struct ListNode* next;    /* Указатель на следующий элемент на текущем уровне */
} ListNode;

Благодаря union узел занимает в памяти ровно столько места, сколько требуется большему из полей data или sub, избегая лишних затрат.

Коротко: главное

  • Списки общего вида - нелинейные структуры, где узел может быть атомом или ссылкой на другой список.
  • Они позволяют моделировать иерархии и разреженные структуры.
  • Ортосписок связывает элементы одновременно по двум измерениям (строкам и столбцам) и идеален для хранения разреженных матриц.
  • На C узлы с разными типами содержимого реализуются через union и флаг типа.

Проверьте себя

Какой инструмент языка C позволяет эффективно хранить в одном поле структуры ЛИБО число-атом, ЛИБО указатель на подсписок?

Как вам урок? Спасибо за отзыв!