50276 (588702), страница 9
Текст из файла (страница 9)
Рассмотренные выше процедуры поиска и вставки элементов в дерево часто используются совместно. Например, с помощью этих процедур можно решать следующие комплексные задачи:
1) поместить данный элемент (не принадлежащий дереву) после указанного элемента дерева;
2) вставить определенный элемент дерева после некоторого элемента того же дерева (переместить элемент).
Следует отметить, что эти операции в дереве поиска надо проводить осторожно, чтобы не нарушить его упорядоченность.
Решение второй задачи может быть реализовано с помощью следующей программы:
program POISK_I_VSTAVKA;
label 1,2,3;
type SS = ^ZVENO;
ZVENO = record
k,n: integer;
left, right: SS;
end;
var DER1,Z,X,T:ss; I,J:integer;
Y:real; O:char;
begin
1:clrscr; gotoxy(20,2);write(' ПОИСК И ВСТАВКА');
writeln; writeln(' ОБЩЕЕ ДЕРЕВО ');writeln;
PRINTTREE(DER1,3,Y); writeln;
writeln('ВСТАВКА HОВОГО ЭЛЕМЕHТА ПОСЛЕ HАЙДЕHHОГО ВЛЕВО');
2:writeln;write('Укажите элемент для вставки: '); readln(I);
POISK(DER1,I,X);
if X^.k <> I then begin
write('Такого элемента нет в деpеве!'); goto 2 end;
3:write('Укажите элемент, за которым идет вставка:');read(j);
POISK(DER1,J,Z); if Z^.k <> J then begin
write('Такого элемента нет в деpеве ! ');
readln;goto 3 end; clrscr;
gotoxy(41,3); write(' ДЕРЕВО до вставки ');
PRINTTREE(DER1,3,Y);
new(T); T^.left:= nil; T^.right:= nil; T^.k:= X^.k;
VSTAVKA(Z,Z^.LEFT,T);
gotoxy(3,3);write(' ДЕРЕВО после вставки ');
PRINTTREE(DER1,3,Y); writeln;
writeln('Вставлен элемент',I:3,'влево после элемента',J:3);
write('Еще ?(y/n): '); readln(O); if O='y' then goto 1
end.
Дерево поиска есть упорядоченное дерево, поэтому для удаления его некоторого элемента необходимо применить принцип удаления, рассмотренный в 15.3.3.
Напомним, что согласно этому принципу, при удалении элемента из дерева на его место ставится любой крайний правый элемент левого поддерева, следующий за удаленным, или любой крайний левый элемент правого поддерева.
procedure UDALEN(var Z, X:ss);
{ X - удаляемый элемент, Z - предшествующий}
var P, M: SS; { Вспомогательные вершины }
begin
¦if X^.left = nil then { Удаление левых листов }
¦ if Z^.left^.k = X^.k
¦ then Z^.left:= X^.right
¦ else Z^.right:= X^.right
¦ else { Удаление правых листьев }
¦ if X^.left^.right = nil then
¦ if Z^.left^.k = X^.k then
¦ begin
¦ ¦ Z^.left:= X^.left;
¦ ¦ X^.left^.right:= X^.right;
¦ end
¦ else begin
¦ ¦ Z^.right:=X^.left;
¦ ¦ X^.left^.right:= X^.right;
¦ ¦ Z^.right:=X^.right
¦ end
¦ else begin {Удал-е внутр. вершин}
¦ ¦ P:=X^.left^.right; M:=X^.left;
¦ ¦ while P^.right <> nil do
¦ ¦ begin M:=P; P:=P^.right; end;
¦ ¦ X^.k:=P^.k;
¦ ¦ M^.right:=nil; {Отрезание листа}
¦ end;
end.
Рассмотренная выше процедура позволяет удалить элемент дерева, для которого известны ссылки на него самого и на предшествующий ему элемент. Однако часто приходится удалять элемент дерева по его значению, а не по ссылке. В этом случае сначала необходимо с помощью процедуры POISK найти ссылку на данный элемент (если он есть в дереве), ссылку на предшествующий ему элемент, а затем по двум ссылкам удалить указанный элемент с помощью процедуры UDALEN.
Все это показано в следующей программе:
program POISK_I_UDALENIE;
label 1,2,3;
type SS = ^ZVENO;
ZVENO = record
k,n: integer;
left, right: SS; end;
var DER,Z,X,T:ss; I,J:integer;
Y:real; O:char;
begin
1:clrscr; gotoxy(20,2); write('ПОИСК И УДАЛЕНИЕ');
writeln(' ДЕРЕВО ПОИСКА '); PRINTTREE(DER,3,Y);
writeln(' УДАЛЕНИЕ УКАЗАННОГО ЭЛЕМЕНТА ');
2:writeln;write('Укажите элемент для удаления: '); readln(I);
POISK(DER,I,X); if X^.k <> I then begin
{ X - ссылка на вершину I }
write('Такого элемента нет в деpеве ! '); readln;goto 2 end;
if X^.k = DER^.k then begin
writeln('ВHИМАHИЕ ! Это - коpень деpева !'); goto 2 end;
3:write('Укажите элемент, перед которым идет удаление:');
readln(J); POISK(DER,J,Z);
{ Z - ссылка на вершину J}
if Z^.k <> J then begin
write('Такого элемента нет в деpеве ! '); goto 3 end;
if (Z^.left^.k <> I) and (Z^.right^.k <> I) then
begin write('Такой паpы элементов нет в деpеве ! ');
goto 3 end; clrscr;
gotoxy(41,3); write(' ДЕРЕВО до удаления '); writeln;
PRINTTREE(der,43,y); UDALEN(Z,X);
gotoxy(3,3);write(' ДЕРЕВО после удаления ');writeln;
PRINTTREE(der,3,y); writeln;
writeln('Удален элемент',i:3,' после элемента ',j:3);writeln;
write('Еще ?(y/n): '); readln(o); if O='y' then goto 1;
end.
15.6 Некоторые дополнительные возможности работы с динамическими структурами
Как мы уже отмечали выше, все динамические структуры, образующиеся в памяти ЭВМ, могут рассматриваться в ОЗУ, отведенной для программы пользователя, как "КУЧА". Пользователь может работать с этой "кучей" с помощью специальных процедур. Среди них самыми важными являются запоминание состояния "кучи" и последующее воспроизведение ее. Это делается с помощью процедур MARK и RELASE.
Мы знаем, что для долговременного хранения информации в файлах используется внешняя память в виде диска или дискеты. Часто возникает необходимость записать на диск созданную динамическую структуру с целью ее сохранения и последующего воспроизведения в ОЗУ.
Для решения этой задачи необходимо представлять себе, что все динамические объекты (списки, стеки, деки, очереди, деревья), как и другие простые данные, лежат в некоторой, строго отведенной области ЭВМ. При этом начальный объект в структуре находится в ячейке памяти с номером N, образованным в результате работы процедуры NEW, а последний - в ячейке памяти с номером N+б (дельта), где б, в каком-то смысле, есть длина динамического объекта. Если знать эти числа, т.е. номера первой и последней ячеек, то можно, не обходя все дерево, переписать содержимое группы ячеек между адресами N и N+б на диск в виде файла. Затем при необходимости есть возможность списать с диска данные не куда-нибудь в память, а именно в ячейки с указанными адресами.
Для решения этой задачи используется функция HEAPPTR, которая возвращает так называемый текущий указатель "кучи", т.е. адрес ее конца. В этом случае достаточно запомнить адрес конца "кучи" в самом начале и по окончании работы с динамическими объектами.
Все эти операции реализованы в следующей программе:
procedure ZAPIS(F: file of integer);
var N,K,I,ZN: integer;
begin
N:= HEAPPTR; { Начало "кучи" }
................
{ Создание динамической структуры }
.................
K:= HEAPPTR; {Конец "кучи"}
rewrite(F); write(F, N);
for i:=1 to k do begin ZN:=MEM[i];
write(F, ZN); end;
close(F);
end.
ПОЯСНЕНИЕ. Первым в файл идет начальный адрес "кучи". Это необходимо для того, чтобы узнать потом, с какого адреса можно воспроизвести "кучу". Далее в процедуре идет имя MEM - стандартное имя массива-памяти. То есть вся память понимается как массив, а ее индексы есть адреса ее точек. Это делается в рамках Паскаля. Вся запись и чтение в памяти идет через одномерный массив.
Рассмотрим теперь процедуру воспроизведения, где данные считываются из файла и записываются в нужные адреса памяти:
procedure VOSPROIZV(F: file of integer; var NACH: SS;)
var ZN, N:integer;
begin reset (F); read(F, ZN);
NACH:= ptr(ZN); n:= zn;
{Начальный адрес заполнения памяти}
while not eof(F) do begin read(F, ZN);
mem[N]:= ZN;
N:= N+1; end;
close(F);
end.
ПРИМЕЧАНИЕ. Здесь PTR - функция, восстанавливающая ссылку на адрес ZN - первый адрес динамической структуры. Эта ссылка запоминается в переменной NACH, после чего процедура может обращаться к динамическому объекту по данной ссылке.
ЛИТЕРАТУРА
-
Йенсен К.В. Паскаль: руководство для пользователя и описание языка. - М.: Финансы и статистика, 1982.
-
Абрамов В.Г., Трифонов Н.П., Трифонова Г.Н. Введение в язык Паскаль.- М.: Наука, 1989.
-
Эрбс Х.Э., Штольц О. Введение в программирование на языке Паскаль.- М.: Наука, 1989.
-
Корноухов М.А., Пантелеев И.В. Справочное руководство по языку программирования TURBO-PASCAL.- М.: Изд-во МГУ им.Ломоносова, 1985.
-
Хершель Р. Турбо Паскаль.- Вологда: МП "МИК", 1991.
ПРИЛОЖЕНИЕ
Настоящее приложение содержит в себе сборник программ практически по всем темам данного учебного пособия. Каждая программа написана с использованием материала, включенного в текст пособия. Большая часть из них представляет собой интегрированный пакет, в который входят рассмотренные в курсе самостоятельные программы, а также процедуры и функции, объединенные в единое целое и посвященные одному разделу учебного пособия. Каждый пакет иллюстрирует работу включенных в нее программных продуктов в их взаимосвязи и при различных исходных данных. Вынесенный в приложение учебный материал поможет студентам лучше разобраться в тонкостях языка Паскаль, а преподавателям использовать его в качестве демонстрационной поддержки читаемого курса.
program RABMAS; uses crt;
const M=10; N=10;
type LINE = array[1..n ] of integer;
TAB = array[1..m] of LINE;
var S,I,J,X,Y:integer; MAS:TAB;
{ ВХОЖДЕНИЕ БКУВ В ТЕКСТ }
procedure COUNTER;
var COUNT: array['a'..'z'] of integer;
CH: char; N: integer;
begin
¦ for CH:= 'a' to 'z' do
¦ COUNT [CH]:= 0; N:= 0;
¦ repeat
¦ ¦ read(CH); N:= N+1;
¦ ¦ if (CH >= 'a') and (CH <= 'z') then
¦ ¦ COUNT [CH]:= COUNT [CH]+1;
¦ until CH = '.'; readln; writeln;
¦ writeln('Частота вхождения букв: '); writeln;
¦ for CH:= 'a' to 'z' do
begin
¦ ¦write(CH,COUNT[CH]:10,COUNT[CH]*100/N:10:2,' ':15);
¦ ¦CH:=succ(CH);
¦ ¦writeln(ch,count[ch]:10,count[CH]*100/N:10:2);
¦ end;
end;
{ ЧИСЛО ДНЕЙ В МЕСЯЦЕ }
procedure NUMBRDAY;
type MONAT = (JAN, FEB, MAR, APR, MAY, JUN, JUL, AUG,
SEP, OKT, NOV, DEC);
var DAY: array [MONAT] of 28..31;
T: MONAT; k:integer;
begin
¦ for T:= JAN to DEC do
¦ case T of
¦ JAN, MAR, MAY, JUL, AUG, OKT, DEC: DAY[T]:= 31;
¦ APR, JUN, SEP, NOV: DAY[T]:= 30;
¦ FEB: DAY[T]:= 28;
¦ end;
¦ writeln(' Число дней в месяцах: '); K:=1;
¦ for T:= JAN to DEC do begin
¦ writeln(' ',K:2,'-й',' месяц ',day[t],' дней ');
¦ K:=K+1;
¦ end;
end;
{ ВВОД, ПЕЧАТЬ И ОБРАБОТКА МАССИВА }
procedure VVODMASSIV(M,N:integer;var MAS:TAB);
begin
¦ for I:=1 to M do
¦ for J:=1 to N do
¦ read(MAS[I][J]); readln;
end;
procedure VIVODMASSIV(M,N:integer;var MAS:TAB);
begin
¦ for I:=1 to M do
¦ begin
¦ ¦ for J:=1 to N do
¦ ¦ write(MAS[I][J]:5,' ');
¦ ¦ writeln;
¦ end;
end;
procedure OBRABOTKA(M,N:integer; MAS:TAB; var SUM:integer);
begin
¦ SUM:= 0;
¦ for I:=1 to M do
¦ for J:=1 to n do
¦ if J > I then SUM:= SUM+MAS[I][J];
end;
{ ОСНОВНАЯ ПРОГРАММА }
begin
clrscr; writeln(' ПОДСЧЕТ ЧИСЛА ВХОЖДЕНИЙ БУКВ В ТЕКСТ');
writeln; write('Введите текст с точкой на конце: ');COUNTER;
readln;clrscr;
writeln(' ЧИСЛО ДHЕЙ В МЕСЯЦАХ ГОДА !');
writeln; NUMBRDAY; READLN;
CLRSCR;writeln('СУММА ЭЛ-ОВ МАССИВА HАД ГЛАВHОЙ ДИАГHАЛЬЮ ');
writeln; write(' Введите число стpок матpицы: '); readln(X);
write(' Введите число столбцов матpицы: ');readln(Y);
write(' Введите чеpез пpобел ',X*Y,' чисел(ла): ');
VVODMASSIV(X,Y,MAS); writeln; writeln;
writeln(' ИСХОДНЫЙ МАССИВ');
VIVODMASSIV(X,Y,MAS); OBRABOTKA(X,Y,MAS,S);writeln;
writeln(' Сумма элементов = ',s);
writeln; write (' К О H Е Ц Р А Б О Т Ы ! ');readln;
end.
program LITERI; uses crt;
procedure SKOBKI;
var c: char; i: integer;
begin
¦ i:=0; read(c); writeln;
¦ write('Строка без скобок: ');
¦ while c <> '.' do
¦ begin
¦ ¦ if c='(' then i:=1
¦ ¦ else if c = ')' then i:=0
¦ ¦ else if i=0 then write(c);
¦ ¦ read(c);
¦ end;
end;
procedure SUITE;