4.Описание структуры программы:
Как и говорилось выше, программа состоит на базе сложной динамической структуры - Дерева со списками. Ниже вы видите раздел Type где описана эта сложная древовидная структура. Дерево строится по категории ПО.
type
ukaz=^spisok;
spisok=record
name:string[14];
lic:string[10];
os:string[10];
lang:string[6];
razrab:string[15];
rate:string[4];
next:ukaz;
end; uk=^derevo;
derevo=record
kat:string[12];
left,right:uk;
spisok:ukaz;
end;
В начале программы в разделе uses подключаем стандартный модуль crt для необходимости очистки экрана clrscr и для увеличения размеров окна процедурами CRTWindowSize(80,40), которая устанавливает размер окна в ширину 80 символов и в высоту 40 строк, и процедурой Window(1,1,80,40), которая определяет на экране текущее окно.
Описание переменных:
В разделе var описаны необходимые глобальные переменные.
var
root:uk;
//корень дерева.
k,kategor,st,filename:string;
// переменные строкового типа.
fl,fl2:boolean;
// переменные логического типа(флаги).
a,b,c,d,e,v,n:integer;
{ вспомогательные переменные
целочисленного типа исп. в основном в
меню и под меню.}
f:text;
// текстовый файл txt.
Данная программа выполняет все основные операции с данными. Ниже написаны основные операции, их описания и процедуры:
1.Добавление программ в базу данных:
Процедура add выполняет запрос категории дерева (предварительно выделяется память под динамическую переменную), проверяется эта категория
процедурой prov_kat, если флагу присваивается значение true (такая категория уже существует в дереве) то добавляется только элементы списка к списку, а если же флаг false то вызывается процедуры zap_spis и add_spisok и категория (эл-т дерева) добавляется в дерево процедурой add_tree.
procedure
add(var root:uk);
var
temp:uk; g,temp_sp:ukaz; n:integer; begin fl:=false; new(temp); with
temp^ do
begin
writeln('
.: Каталог ПО ПК :.');
write('Введите
категорию ПО:');
readln(kat);
spisok:=nil;
left:=nil;
right:=nil;
end; prov_kat(root,g,fl,temp^.kat);
if fl=true then
begin
writeln('Добавить
в эту категорию данные:');
writeln('1.Да');
writeln('2.Нет');
read(n);
clrscr;
while n=1 do
begin
zap_spis(temp_sp);
g^.next:=temp_sp;
g:=g^.next;
writeln('Добавить
в эту категорию данные:');
writeln('1.Да');
writeln('2.Нет');
readln(n);
clrscr;
end;
end else
begin
writeln('Добавить в
эту категорию данные:');
writeln('1.Да');
writeln('2.Нет'); read(n); while
n=1 do begin clrscr; zap_spis(temp_sp); add_spisok(temp,temp_sp);
writeln('Добавить в
эту категорию данные:');
writeln('1.Да');
writeln('2.Нет'); readln(n); end; add_tree(temp);
end;
end;
Процедура prov_kat выполняет проверку категории, если категория уже существует
в дереве то флагу присваивается значение true и ищется конец списка g1, которая передается в программу. Имеет логарифмическую эффективность.
procedure
prov_kat(var p:uk; var g1:ukaz; var fl:boolean; k:string); begin if
p<>nil then if
p^.kat=k then begin
fl:=true;
g1:=p^.spisok;
while
g1^.next<>nil do
g1:=g1^.next;
end
else
if k<p^.kat
then prov_kat(p^.left,g1,fl,k)
else
prov_kat(p^.right,g1,fl,k);
end;
Процедура zap_spis: создается динамическая переменная для списка и выполняется запрос элементов списка (запрос информационных полей).
procedure
zap_spis(var temp_sp:ukaz); begin new(temp_sp); with
temp_sp^ do
begin
writeln('
.: Каталог ПО ПК :.');
write('Введите
название ПО:');
readln(name);
write('Укажите какая
лицензия:');
readln(lic);
write('Укажите какая
OS поддеоживается:');
readln(os);
write('Введите
яз[ru/en]:');
readln(lang);
write('Введите
Разработчика:');
readln(razrab);
write('Введите
рейтинг ПО:');
readln(rate);
next:=nil;
end;
end;
Процедура add_spisok выполняет присоединение элементов списка к элементу дерева(к категории). Добавление в список по типу стека. Имеет линейную эффективность.
procedure
add_spisok(var temp:uk; var temp_sp:ukaz); begin if
temp^.spisok=nil then
temp^.spisok:=temp_sp
else
begin
temp_sp^.next:=temp^.spisok;
temp^.spisok:=temp_sp;
end;
end;
Процедура add_tree выполняет добавление категории к двоичному дереву по правилу двоичного дерева. Имеет логарифмическую эффективность.
procedure
add_tree(var temp:uk);
var
p,p1:uk; begin if
root=nil then root:=temp
else
begin
p:=root;
while p<>nil do
begin
p1:=p;
if temp^.kat<p^.kat
then p:=p^.left
else
p:=p^.right;
end;
if temp^.kat<p1^.kat
then p1^.left:=temp
else
p1^.right:=temp;
end;
end;
2.Удаление категории ПО с включенными в неё списком программ:
В основной программе запрашивается имя удаляемой категории и вызывается процедура Delete_kat. Если введенной категории нету в БД, то выводиться сообщение «Данной категории нет в БД». Процедура Delete_kat – рекурсивная процедура, которая удаляет категорию ПО (элемент дерева).
При этом удаление вершины дерева (категории) реализуется в зависимости от того, какая вершина удаляется:
1.Удаляемая вершина не содержит поддеревьев (лист). При этом удаляется ссылка на удаляемую вершину из родительской вершины.
2.Удаляемая вершина содержит одну ветвь. Для удаления необходимо скорректировать соответствующую ссылку в родительской вершине, заменив адрес удаляемой вершины адресом вершины, из нее исходящей.
3.Удаляемая вершина содержит две ветви. В этом случае нужно найти подходящую вершину, которую можно вставить на место удаляемой, причем эта вершина должна легко перемещаться. Такая вершина всегда существует: либо это самый правый элемент левого поддерева, либо – самый левый элемент правого поддерева.
Procedure
Delete_kat(var r:Uk; kat:string);
Procedure
DEL(var r:Uk; q:Uk);
Var
q1:Uk; Begin
if r^.right=nil then
begin
q^.kat:=r^.kat;
q1:=r;
r:=r^.left;
dispose(q1); end
else
DEL(r^.right,q); End;
Var
q:Uk; Begin
if r=nil
then writeln('Данной
категории
нету
в
БД!')
else
if kat<r^.kat then
DELETE_kat(r^.left,kat)
else
if kat>r^.kat
then DELETE_kat(r^.right,kat)
else
begin
if
r^.right=nil then
begin
q:=r;
r:=r^.left;
dispose(q);
end
else
if
r^.left=nil then
begin
q:=r;
r:=r^.right;
dispose(q);
end
else
DEL(r^.left,r);
end;
end;
3.Удаление программы из определенной категории:
В основной программе запрашивается имя категории с которой будет удаляться программа и название программы. А далее вызывается процедура spisok_del.
В случае ввода категории и названия ПО, когда их нет в БД выводится следующее сообщение «Такой категории нет в БД» или «Данной программы нет в БД!»
Процедура spisok_del – рекурсивная процедура, которая удаляет ПО из категории ПО БД.
(удаляет эл-т списка из эл-та дерева).
procedure
spisok_del(p:uk;kat,k:string;var fl:boolean);
var
g,g1:ukaz; Begin if
p<>nil then if
p^.kat=kategor then
begin
fl:=true;
if p^.spisok^.next=nil then
Delete_kat(root,kat)
else
begin
g1:=p^.spisok;
while (g1<>nil) and
(g1^.name<>k) do
begin
g:=g1;
g1:=g1^.next;
end;
if g1=nil then
writeln('Данной программы нет в БД!')
else
begin
if p^.spisok=g1 then
p^.spisok:=g1^.next
else
if g1^.next=nil then
g^.next:=nil
else
g^.next:=g1^.next;
end;
writeln;
end;
end
else
if kategor<p^.kat then
spisok_del(p^.left,kat,k,fl)
else
spisok_del(p^.right,kat,k,fl);
end;
4. Удаление всей базы данных:
При выборе этой операции будет выведено следующий запрос: «Вы действительно хотите удалить всю БД?»
Процедура Del_tree рекурсивно удаляет всю БД (уничтожает дерево).
Procedure
Del_tree(var p:Uk);
Begin
If p<>nil then begin
Del_tree(p^.left);
Del_tree(p^.right);
Dispose(p);
p:=nil;
End;
End;
5. Вывод БД в виде таблицы.
Процедура vivod выводит БД на экран в виде таблицы. Процедура представляет собой рекурсивный обход дерева – слева направо (инордер).
procedure
vivod(p:uk);
var
g:ukaz; begin if
p<>nil then
begin
vivod(p^.left);
g:=p^.spisok;
writeln('|------------|--------------|----------|----------|------|---------------|----|');
fl:=false;
while g<>nil do
begin
if fl=false then
begin
fl:=true;
write('|',p^.kat:12,'|');
end
else
write('|','':12,'|');
write(g^.name:14,'|');
write(g^.lic:10,'|');
write(g^.os:10,'|');
write(g^.lang:6,'|');
write(g^.razrab:15,'|');
write(g^.rate:4,'|');
writeln;
g:=g^.next;
end;
vivod(p^.right);
end;
end;
Процедура poisk считывает из строки данные и присваивается строковой переменной.
procedure
poisk(var st,k:string);
var
poz:integer; begin poz:=pos('
',st); k:=copy(st,1,poz-1); delete(st,1,poz); end;
6. Данная программа так же производит поиск в базе данных по всем критериям: по категории программы, по названию программы, по типу лицензии, по поддерживаемым операционным системам, по языку интерфейса программы, по разработчику программного обеспечения и по минимальной границе рейтинга.
Процедура poisk_kat – рекурсивный обход дерева, выполняет вывод всех программ в виде таблицы из определенного каталога, т.е. поиск по категории ПО. В основной программе запрашивается категория, которую ищите, если же данной категории нет, то выводиться соответствующее сообщение.
procedure
poisk_kat(var p:uk);
var
g:ukaz; begin if
p<>nil then if
p^.kat=kategor then
begin
g:=p^.spisok;
fl:=false;
while g<>nil
do
begin
if fl=false then
begin
write('|',p^.kat:12,'|');
fl:=true;
end
else
write('|','':12,'|');
write(g^.name:14,'|');
write(g^.lic:10,'|');
write(g^.os:10,'|');
write(g^.lang:6,'|');
write(g^.razrab:15,'|');
write(g^.rate:4,'|');
writeln;
g:=g^.next;
end;
end
else
if
kategor<p^.kat then poisk_kat(p^.left)
else
poisk_kat(p^.right);
end;
Процедура poisk_name выполняет поиск по названию ПО и выводит в виде таблицы. Рекурсивный обход дерева – слева направо (инордер). Процедуры поиска по типу лицензии, по языку, по разработчику, по ОС и по минимальной границе рейтинга имеют аналогичный вид. Как и в случае с поиском по категории, в основной программе запрашивается имя критерии, которую ищите, если же данной критерии нет, то выводиться соответствующее сообщение.
procedure
poisk_name(var p:uk);
var
g:ukaz; begin if
p<>nil then
begin
poisk_name(p^.left);
g:=p^.spisok;
fl2:=false;
while g<>nil do
begin
if g^.name=k then
begin
fl:=true;
if fl2=false
then
begin
fl2:=true;
writeln('|------------|--------------|----------|----------|------|---------------|----|');
write('|',p^.kat:12,'|')
end
else
write('|','':12,'|');
write(g^.name:14,'|');
write(g^.lic:10,'|');
write(g^.os:10,'|');
write(g^.lang:6,'|');
write(g^.razrab:15,'|');
write(g^.rate:4,'|');
writeln;
end;
g:=g^.next;
end;
poisk_name(p^.right);
end;
end;
7. Для хранения базы данных на внешних носителях в программе есть под меню работа с текстовыми файлами и управления над ними: считывание базы данных с текстового файла в оперативную память ПК:
Процедура readfromfile выполняет чтение из файла. Эта процедура почти идентична с процедурой add за исключением того что данные считываются с файла.
Выделяется динамическая память под категорию ПК, ищется в файле этот элемент и проверяется процедурой prov_kat существует такая категория или нет. Если существует, то добавляется только элементы списка в список, а иначе список присоединяется к элементу дерева (к категории), которая потом добавляется дерево.
Процедура poisk является вспомогательной процедурой для считывания из файла.
При выборе этой операции вам будет предложено ввести имя файла, для считывания из него информации. Если файла не существует, то программа выдаст вам сообщение, что данного файла не существует. После успешного ввода имени файла вся БД из файла считывается в оперативную память в виде дерева со списками и с ними можно работать, т.е. выполнять различные операции над ними.
Procedure
readfromfile(var root:uk);
var
p,p1,temp:uk; g,g1,temp_sp:ukaz; k:string; begin while
not eof(f) do begin new(temp); fl:=false; readln(f,st); poisk(st,k); temp^.kat:=k; prov_kat(root,g1,fl,k); if
fl=true then begin read_spisok(temp_sp); g1^.next:=temp_sp; end else begin temp^.spisok:=nil; temp^.left:=nil; temp^.right:=nil; read_spisok(temp_sp); temp^.spisok:=temp_sp;
add_tree(temp); end;
end;end;
Процедура read_spisok: создает динамическую переменную и выполняет считывание элементов списка из файла.
procedure
read_spisok(var temp_sp:ukaz); begin new(temp_sp); poisk(st,k); temp_sp^.name:=k; poisk(st,k); temp_sp^.lic:=k; poisk(st,k); temp_sp^.os:=k; poisk(st,k); temp_sp^.lang:=k; poisk(st,k); temp_sp^.razrab:=k; poisk(st,k); temp_sp^.rate:=k;
temp_sp^.next:=nil;end;
8. Вывод (сохранение) БД в текстовый файл.
Процедура vivod_v_fail сохраняет БД в файл. Рекурсивный обход дерева – сверху вниз (преордер). В основной программе перед выводом БД требуется ввести имя файла, куда будет сохраняться БД, если файл с таким именем уже существует, то программа предложит перезаписать файл.
procedure
vivod_v_fail(p:uk);
var
g:ukaz; begin if
p<>nil then
begin
g:=p^.spisok;
while g<>nil do
begin
write(f,p^.kat,'
');
write(f,g^.name,'
');
write(f,g^.lic,'
');
write(f,g^.os,'
');
write(f,g^.lang,'
');
write(f,g^.razrab,'
');
write(f,g^.rate,'
');
writeln(f);
g:=g^.next;
end;
vivod_v_fail(p^.left);
vivod_v_fail(p^.right);
end;
end;
9. Удаление файла. В данной программе можно так же удалить любой текстовый файл. При этом выполняется запрос на имя удаляемого файла. Если такого файла нет, то программа выведет соответствующее сообщение, если файл существует, программа сделает запрос «Вы действительно хотите удалить файл?» и решение будет за вами.
И так же при выходе из программы вам предлагается сохранить данные.
В общем, все процедуры связанные с обработкой двоичного дерева поиска имеют логарифмическую эффективность, кроме процедур вывода всей БД и поисков из БД, которые имеют линейную эффективность.
Для выбора нужной операции в меню нужно нажать цифру, соответствующую номеру операции и нажать на клавишу Enter.
