Энциклопедия Turbo Pascal. Главы 1-4
Страница 34. Использование связанного списка для организации разреженного  массива


Использование связанного списка для организации разреженного  массива

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

     str128 = string[128];
     str9 = string[9];

     CellPointer = ^cell;

     cell = record
       CellName: str9; {содержит название ячейки}
       formula: str128; {содержит формулу}
       next: CellPointer; {указатель на следующую запись}
       prior: CellPointer; {указатель на предыдущую запись}
     end;

     В этом примере поле "CellName" содержит строку  с  названием
ячейки, например, А1, В34 или Z19. Строка "formula" содержит фор-
мулу для каждой ячейки электронной таблицы.  Ниже приводятся нес-
колько  примерных  программ,  использующих разреженные матрицы на
базе связанного списка.  (Следует помнить, что имеется много спо-
собов реализации электронных таблиц. К приводимым ниже структурам
данных и программам следует относиться только как к примерам  ис-
пользования таких методов). Для указателей начала и конца связан-
ного списка используются следующие глобальные переменные:

     start, last: CellPointer;

     Когда вы  вводите формулу в ячейку типичной электронной таб-
лицы,  вы фактически создаете новый элемент разреженной  матрицы.
Если  электронная таблица строится на базе связанного списка,  то
вставка нового элемента будет производится с помощью функции "DLS
_Store",  которая  рассматривается в главе 2.  (Поскольку Паскаль
позволяет создавать независимые функции указанная  функция  может
использоваться  фактически  без  всяких изменений).  В приводимом
примере список сортируется по названию ячейки (т.е. А12 предшест-
вует А13 и т.д.)

{ упорядоченная вставка элементов в список и установка указателя
          на начало списка }
          function DLS_Store(info, start: CellPointer;
                           var last: CellPointer): CellPointer;
          var
            old, top: CellPointer;
            done: boolean;
          begin
            top := start;
            old := nil;
            done := FALSE;

            if start = nil then begin { первый элемент списка }
              info^.next := nil;
              last := info;
              info^.prior :=nil;
              DLS_Store := info;
            end else
            begin
              while (start<>nil) and (not done) do
              begin
               if start^.CellName < info^.CellName then
               begin
                 old := start;
                 start := start^.next;
               end else
               begin { вставка в середину }
                 if old <>nil then
                   begin
                   old^.next := info;
                   info^.next := start;
                   start^.prior := info;
                   info^.prior := old;
                   DLS_Store := top; { сохранение начала }
                   done := TRUE;
                 end else
                 begin
                   info^.next := start;{новый первый элемент }
                   info^.prior := nil;
                   DLS_Store := info;
                   done := TRUE;
                 end;
               end;
              end;  { конец цикла }
              if not done then begin
               last^.next := info;
               info^.next := nil;
               info^.prior := last;
               last := info;
               DLS_Store := top; { сохранение начала }
              end;
            end;
          end;  { конец функции DLS_Store }

     Для удаления элемента из электронной таблицы необходимо уда-
лить соответствующую запись из списка и возвратить  занимаемую им
память системе с помощью функции Dispose.  Функция DL_Delete уда-
ляет ячейку из списка по заданному названию:

    { удаление ячейки из списка }
     function DL_Delete(start: CellPointer;
                     key str9): CellPointer;
     var
       temp, temp2: CellPointer;
       done: boolean;
     begin
       if start^.CellName=key then
       begin { первый элемент в списке }
        DL_Delete := start^.next;
        if temp^.next <> nil then
        begin
          temp :=  start^.next;
          temp^.prior :=  nil;
        end;
        Dispose(start);
       end else
       begin
        done :=  FALSE;
        temp :=  start^.next;
        temp2 :=  start;
        while (temp<>nil) and (not done) do
        begin
          if temp^.CellName=key then
          begin
            temp2^.next :=  temp^.next;
            if temp^.next<>nil then
              temp^.next^.prior :=  temp2;

          done :=  TRUE;
          last :=  temp^.prior;
          Dispose(temp);
        end else
        begin
          temp2 :=  temp;
          temp :=  temp^.next;

       end;
       end;
       DL_Delete :=  start; { начало не изменяется }
       if not done then WriteLn('not found');
     end;
   end; { конец процедуры удаления элемента из списка }

     Функция Find позволяет обратиться к любой конкретной ячейке.
Эта функция имеет важное значение,  так как многие формулы элект-
ронной  таблицы  имеют ссылки на другие ячейки и нужно эти ячейки
найти,  чтобы обновить их значения.  В этой функции  используется
линейный поиск и,  как показано в главе 2, среднее число операций
при линейном поиске равно n/2,  где n является числом элементов в
списке.  Кроме  того,  значительные потери будут из-за того,  что
каждая ячейка может содержать ссылки на  другие  ячейки  и  тогда
потребуется выполнить поиск этих ячеек.  Ниже дается пример функ-
ции Find (см.след.стр.).

     Создание, поддержка  и  обработка разряженных массивов имеет
один большой недостаток, когда такой массив строится на базе свя-
занного  списка.  Этот недостаток заключается в необходимости ис-
пользовать линейный поиск каждой ячейки списка. Без

     { найти конкретную ячейку и установить указатель на нее }
     function Find(cell: CellPointer): CellPointer;
     var
       c: CellPointer;
     begin
       c :=  start;
       while c<>nil do
       begin
        if c^.CellName=cell^.CellName then find:=c
        else c :=  c^.next;
       end;
       WriteLn('cell not found');
       Find:=nil;
     end; { конец процедуры поиска }


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

 
« Предыдущая статья