48725 (Распределение памяти), страница 2

2016-07-30СтудИзба

Описание файла

Документ из архива "Распределение памяти", который расположен в категории "". Всё это находится в предмете "информатика" из 1 семестр, которые можно найти в файловом архиве . Не смотря на прямую связь этого архива с , его также можно найти и в других разделах. Архив можно найти в разделе "курсовые/домашние работы", в предмете "информатика, программирование" в общих файлах.

Онлайн просмотр документа "48725"

Текст 2 страницы из документа "48725"

Procedure rotate(var p1, p2, p3: ^celltype);

Var

Temp: celltype;

Begin

Temp:=p1;

P1:=p2;

P2:=p3;

P3:=temp;

End;

Теперь вернёмся к описанию нерекурсивной процедуры маркировки. Для неё возможны два состояния: продвижение вперёд, представленное меткой state1 и отход представленный меткой state2 и отход представленный меткой state2. Поначалу, а также в тех случаях, когда мы перешли на новую ячейку (либо в результате шага продвижения вперёд, либо в результате шага переключения), мы переходим в первое состояние. В этом состоянии мы пытаемся выполнить ещё один шаг продвижения вперёд и "отходим" или "переключаемся" лишь в том случае, если оказываемся заблокированными. Можно оказаться заблокированными по двум причинам: (1) ячейка к которой только, что обратились , уже помечена; (2)в данной ячейке отсутствуют ненулевые указатели. Оказавшись заблокированными, переходим во второе состояние - состояние отхода. Переход во второе состояние возможен в тех случаях, когда мы отходим или когда не можем оставаться продвижения вперёд, так как оказались заблокированными. В состоянии отхода проверяем, отошли ли обратно на ячейку source1. Как уже было сказано выше, мы распознаём эту ситуацию по выполнению условия previous=current; в этом случае переходим в состояние 3 (метка state3), то есть практически закончили маркировку ячеек. В противном случае принимается решение либо отступить и остаться в состоянии отхода, либо переключится и перейти в состояние продвижения вперёд.

А ) движение вперёд


L



Previous

current


На рисунках старые указатели показаны сплошными линиями, а новые пунктирными.

Б) Переключение



R



previous


current


В) Отход




current

previous


Авторы используют следующие обозначения: PP - указатель/указатель; PA - указатель/атом и т.д. Кроме того можно заметить, что текст записанный ниже, это не вполне Паскаль-программа. Там не всё в порядке с оператором возврата из процедуры. Например return(false); это авторы делают для упрощения записи. Понимать этот оператор надо так:

Имя функции:=false;

Goto метка конца тела функции.

function blockleft (cell:celltype):boolean;

{Проверяет, является ли левое поле атомом или нуль указателем}

begin

with cell do

if (pattern=pp) or (pattern=pa) then

if left<>nil then return(false);

return true;

end; {blockleft}

function blockright (cell:celltype): boolean;

{ Проверяет, является ли левое поле атомом или нуль указателем }

begin

with cell do

if (pattern=pp) or (pattern=ap) then

if right<>nil then return(false);

end;{blockright}

function block (cell:celltype):boolean;

{проверяет, помечена ли ячейка и не содержит ли ненулевые указатели}

begin

if (cell.mark=true) or blockleft(cell) and blockright(cell) then

return true

else return false

end; {block}

procedure nrdfs; {помечает ячейки, доступные из ячейки source}

var

current, previous:^celltype;

begin {инициализация}

current:=source1^.right; {ячейка на которую указывает source1}

previous:=source1;

source1^.back:=r;

source1^.right:=source1;

source1^.mark:=true;

state1: {Движение вперёд}

if block (current^) then

begin {Подготовка к отходу}

current^.mark:=true;

goto state2;

end;

else

begin

{пометить и продвинуться вперёд}

current^.mark:=true;

if blockleft (current^) then

begin

{следование правому указателю}

current^.back:=r;

rotate (previous, current, current^.right);

{реализация изменения согласно схеме а}

goto state1

end

else

begin

{следование левому указателю}

current^.back:=L;

rotate(previous, current, current^.left);

{реализация изменения согласно схеме а}

goto state1;

end;

end;

state2: {Завершение отход или переключение}

if previous = current then goto state3 {завершение}

else if (previouse^.back=L) and (not blockright(previous^)) then

begin {переключение}

previous^.back:=R;

rotate(previouse^.left,current, previouse^.right);

{реализация изменений как на схеме б}

goto state1;

end

else if previous^.back=R then {Отход}

rotate(previous, previous^.right,current)

{реализация изменений как на схеме в}

else rotate(previous, previous^.left,current);

{реализация изменений вариант в, но поле left предыдущей ячейки включено в путь}

goto state2

end;

state 3: {Здесь необходимо вставить код для связывания непомеченных ячеек в список свободной памяти}

end; {nrdfs}

2 Задача 2. Пометка занятых ячеек памяти

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

В чём проблема.

Задача пометки упирается в задачу обхода списка. Если мы научимся обходить связный список, то проблема пометки решится сама собой. Задача же обхода произвольного списка имеет тривиальное решение. А именно, мы можем в каждом узле имеющем некоторое количество указателей ВПЕРЁД завести такое количество указателей НАЗАД и счётчик вхождений в данный узел. Следующий алгоритм будет решением задачи:

При вхождении в узел

Если есть неиспользованные указатели ВПЕРЁД

ТО перейти на узел по очередному указателю ВПЕРЁД

ИНАЧЕ

Если есть неиспользованные указатели НАЗАД

ТО перейти на узел по очередному указателю НАЗАД

Это очень общий алгоритм и мы не будем рассматривать его детально, так как он всё равно не годится из-за необходимости заводить большое количество дополнительных указателей. Вспомним, что ранее рассмотренный алгоритм (алгоритм ДОЙЧА) имеет смысл только потому, что он требует на два указателя лишь одного дополнительного поля. А стало быть проблема заключается в том, что нам нужен алгоритм не требующий больших ресурсов памяти для своей работы.

Небольшой предварительный анализ

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

Идея.

Я думаю, что намек уже понятен. Если мы заведем дополнительное поле размером в один байт, то это даст нам возможность пронумеровать 256 указателей.

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

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

Во-вторых, каким-то образом для каждого узла определено сколько у него указателей, например для хранения этой информации заведена ещё одна переменная.

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

Общее описание алгоритма.

Для того, чтобы иметь возможность двигаться по сети узлов в двух направлениях нужно иметь два рабочих указателя. Назовём их ВПЕРЁД и ОБРАТНО. Указатель ВПЕРЁД содержит адрес узла в который необходимо перейти на следующем шаге, а указатель ОБРАТНО содержит адрес узла из которого Исполнитель вышел на предыдущем шаге. Сие означает, что на каждом шаге (в каждом узле) нужно выполнить операции определения этих адресов.

Рассмотрим некий узел, назовём его Текущий. Когда Исполнитель зайдет в этот узел первый раз, он должен будет перейти на узел чей адрес хранится в указателе1. То есть ВПЕРЕД=Указатель1. Понятно, что это первое и последнее использование указателя Указатель1. Он больше для запоминания пути вперёд не нужен, а стало быть его теперь можно использовать для запоминания пути назад, для чего можно выполнить операцию Указатель1=ОБРАТНО.

Когда исполнитель зайдёт в текущий узел второй раз он тоже самое проделает с указателем2. Это исполнитель будет проделывать до тех пор пока есть указатели ВПЕРЁД которыми он ещё не пользовался. А вот дополнительное однобайтовое поле (назовём его счетчик) как раз и пригодится для запоминание номера указателя которым ещё не пользовались.

Перед началом работы проинициализируем поле Счетчик всех узлов сети нулями. Затем каждый раз при входе в очередной узел будем увеличивать значение счётчика на 1. Тогда его значение будет определять номер неиспользованного указателя.

Рано или поздно исполнитель израсходует все указатели и попав в наш текущий узел в очередной раз обнаружит, что вперёд идти некуда, а стало быть нужно идти назад. Если исполнитель впервые пришел к такому выводу, то очевидно путь назад хранится в последнем указателе. Если исполнитель уже второй раз в данном узле решил идти назад, то адрес его пути хранится в предпоследнем указателе и т.д.

Иначе говоря

Идя вперёд исполнитель использует все указатели узла последовательно начиная с первого, занося в них адреса из указателя ОБРАТНО. Когда исполнитель идёт назад он использует указатели в обратном порядке. Относительно динамики изменения счётчика можно сказать, что пока в узле есть неиспользованные указатели вперёд, счётчик растёт (+1 на каждом шаге). Когда начинается движение назад, счётчик убывает (-1 на каждом шаге).

Аналогия с лабиринтом

Представьте себя в лабиринте в котором узлу соответствуют комнаты, а указатели это коридоры. Счетчик это некоторая доска на которой мы можем записывать число и кроме того у нас есть возможность соединять коридоры линиями. Чтобы корректно проверить весь лабиринт мы должны обойти все коридоры по порядку и на каждом шаге коридор из которого вошли в комнату соединять направленным отрезком с тем коридором в который собираемся уйти. А номер коридора в который идти мы будем определять по числу написанному на доске. Когда не останется ни одного не пройденного коридора, мы начиная с последнего и до первого будем выполнять следующее:

  1. Выбираем очередной коридор.

  2. Определяем, с каким коридором он связан (указатель ОБРАТНО) и уходим по нему.

Алгоритм

Тек_узел=Первый узел

Свежие статьи
Популярно сейчас
Почему делать на заказ в разы дороже, чем купить готовую учебную работу на СтудИзбе? Наши учебные работы продаются каждый год, тогда как большинство заказов выполняются с нуля. Найдите подходящий учебный материал на СтудИзбе!
Ответы на популярные вопросы
Да! Наши авторы собирают и выкладывают те работы, которые сдаются в Вашем учебном заведении ежегодно и уже проверены преподавателями.
Да! У нас любой человек может выложить любую учебную работу и зарабатывать на её продажах! Но каждый учебный материал публикуется только после тщательной проверки администрацией.
Вернём деньги! А если быть более точными, то автору даётся немного времени на исправление, а если не исправит или выйдет время, то вернём деньги в полном объёме!
Нет! Мы не выполняем работы на заказ, однако Вы можете попросить что-то выложить в наших социальных сетях.
Добавляйте материалы
и зарабатывайте!
Продажи идут автоматически
4123
Авторов
на СтудИзбе
667
Средний доход
с одного платного файла
Обучение Подробнее