Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
metodichka_SI.doc
Скачиваний:
30
Добавлен:
23.02.2015
Размер:
2.05 Mб
Скачать

1 Задача

В программе реализовать следующие возможности:

1) ввод РМ с клавиатуры

2) сложить две РМ

3) транспонировать РМ

4) умножить РМ на скаляр

5) конвертировать из формата "Обычный" в формат "РМ" и обратно

6) скопировать одну РМ в другую

Для каждой матрицы, с которой работает программа, создается отдельный файл с ее именем. Возможны два формата для записи разреженной матрицы (РМ) в файл:

Формат "РМ"

Набор чисел: номер строки, номер столбца, значение элемента.

Этих наборов в файле столько, сколько ненулевых элементов в матрице.

Будем называть далее нетривиальные (ненулевые) элементы Элементами, а строки, содержащие Элементы - Строками.

Такие названия будут использоваться именно тогда, когда необходимо подчеркнуть не тривиальность элементов и строк.

Нумерация строк и столбцов начинается с нуля.

Пример:

0 0 1

0 1 1

0 2 7

0 4 1

1 2 1

2 4 1

Формат "Обычный"

количество строк N, количество столбцов M, далее N x M чисел - элементы матрицы.

Пример:

3 5

1 1 7 0 1

0 0 1 0 0

0 0 0 0 1

Основным при работе с РМ является формат "РМ" в силу его экономности.

Формат "Обычный" нужен лишь для возможности наглядного и привычного представления матрицы и проверки правильности работы программы.

Чтобы различать эти форматы при работе с файлами, в формате "Обычный" РМ записывается в файл с расширением ".txt",

а в формате "РМ" РМ записывается в файл с расширением ".dat".

Если Вы ввели с клавиатуры имя матрицы m1, то работа осуществляется с файлом "m1.dat".

При вызове пунктов меню 2)-6) на экран никакие результирующие матрицы не выводятся,

всё происходит на уровне работы с файлами.

#include <stdio.h>

#include <stdlib.h>

#include <string.h>

#include <conio.h>

// Строка РМ

struct TRow_struct

{

// номер строки

int num;

// указатель на первый Элемент

struct TEl_struct*El;

// указатель на следующую Строку

struct TRow_struct*next;

};

// Элемент Строки

struct TEl_struct

{

// номер столбца, в котором находится элемент

int num;

// значение элемента

int value;

// указатель на следующий Элемент данной Строки

struct TEl_struct*next;

};

// для удобства сделаны такие определения типов

typedef struct TRow_struct TRow;

typedef struct TEl_struct TEl;

// инициализировать Строку

void initRow(TRow*&r)

{

// выделение памяти

r = (TRow*)malloc(sizeof(TRow));

// пока ТЭлементов не введено

r->El = NULL;

// пока следующей Строки нет

r->next = NULL;

}

// инициализировать Элемент

void initEl(TEl*&r)

{

// выделение памяти

r = (TEl*)malloc(sizeof(TEl));

// пока следующего Элемента нет

r->next = NULL;

}

// загрузить РМ m из файла с именем s (формат "Обычный")

void LoadRM_txt(char s[], TRow*&m)

{

FILE*f;

// открыть текстовый файл на чтение

f=fopen(s,"r");

// инициализировать первую Строку

initRow(m);

int N,M;

// считывание размеров матрицы

fscanf(f,"%d",&N);

fscanf(f,"%d",&M);

int i,j;

int r;

// вспомагательные переменные: "первая Строка", "первый Элемент в Строке"

int FirstRow=1,FirstInRow;

// текущая Строка

TRow*curRow=m;

// текущий Элемент

TEl*curEl;

// считывание в цикле всей матрицы из файла

for (i=0; i<N; i++)

{

FirstInRow=1;

for (j=0; j<M; j++)

{

fscanf(f,"%d",&r);

// если элемент ненулевой

if (r!=0)

{

// если это первый Элемент в текущей Строке

if (FirstInRow)

{

// если Строка не первая, то инициализировать новую Строку

if (!FirstRow)

{

initRow(curRow->next);

curRow=curRow->next;

}

else

{

FirstRow=0;

}

// номер строки

curRow->num=i;

// инициализация элемента

initEl(curRow->El);

curEl = curRow->El;

// номер столбца

curEl->num=j;

// значение

curEl->value = r;

FirstInRow=0;

}

// если Элемент не первый в текущей Строке

else

{

// инициализация нового Элемента

initEl(curEl->next);

curEl = curEl->next;

// номер столбца

curEl->num=j;

// значение

curEl->value = r;

}

}

}

}

// закрыть файл

fclose(f);

}

// найти размеры NxMразреженной матрицыm

void FindSize(TRow*m,int*N,int*M)

{

TEl*El;

*N=0;

*M=0;

while (m!=NULL)

{

El = m->El;

while (El->next!=NULL)

El = El->next;

if (El->num > *M)

*M = El->num;

*N = m->num;

m = m->next;

}

++*N;

++*M;

}

// сохранить РМ m в файл с именем s в формате "Обычный"

void SaveRM_txt(char s[],TRow*m)

{

FILE*f;

// открыть текстовый файл на чтение

f=fopen(s,"w");

int N,M;

// найти размеры

FindSize(m,&N,&M);

// вывести их в файл

fprintf(f,"%d %d\n",N,M);

// Далее - несложный, но аккуратный вывод матрицы в файл

TEl*El;

int i,j;

i = 0;

while (m != NULL)

{

while (i < m->num)

{

for (j=0; j<M-1; j++)

fprintf(f,"0 ");

fprintf(f,"0\n");

i++;

}

El = m->El;

j = 0;

while (El != NULL)

{

while (j < El->num)

{

fprintf(f,"0 ");

j++;

}

fprintf(f,"%d",El->value);

if (El->num != M-1)

fprintf(f," ");

El = El->next;

j++;

}

if (j<=M-1)

{

while (j < M-1)

{

fprintf(f,"0 ");

j++;

}

fprintf(f,"0\n");

}

else

fprintf(f,"\n");

m = m->next;

i++;

}

while (i<N)

{

for (j=0; j<M-1; j++)

fprintf(f,"0 ");

fprintf(f,"0\n");

i++;

}

// закрыть файл

fclose(f);

}

// загрузить РМ mиз файла с именемFName(формат "РМ")

void LoadRM_dat(char FName[],TRow*&m)

{

FILE*f;

// открыть текстовый файл на чтение

f=fopen(FName,"r");

// инициализировать первую Строку

initRow(m);

// первая Строка вспомогательная, все Элементы будут находится в следующих Строках

m->num = -1;

TRow*r,*rL;

TEl*El,*ElL;

initEl(m->El);

// первый Элемент вспомогательный

m->El->num = -1;

int i,j,value;

// пока не достигнут конец файла, считываем наборы из трёх чисел

while (fscanf(f,"%d %d %d",&i,&j,&value) != EOF)

{

// Далее - находим в нашей структуре из Строк и Элементов место для считанного Элемента со значением value

r = m;

rL = r;

while ((r->num < i) && (r->next != NULL))

{

rL = r;

r = r->next;

}

if (r->num == i)

{

El = r->El;

ElL = El;

while ((El->num < j) && (El->next != NULL))

{

ElL = El;

El = El->next;

}

if (El->num > j)

El = ElL;

ElL = El->next;

initEl(El->next);

El = El->next;

El->num = j;

El->value = value;

El->next = ElL;

}

else

{

if (r->num > i)

r = rL;

rL = r->next;

initRow(r->next);

r = r->next;

r->num = i;

r->next = rL;

initEl(r->El);

El = r->El;

El->num = -1;

initEl(El->next);

El = El->next;

El->num = j;

El->value = value;

}

}

// удалить вспомогательную Строку

m = m->next;

r = m;

while (r != NULL)

{

// удалить вспомогательный Элемент

r->El = r->El->next;

r = r->next;

}

// закрыть файл

fclose(f);

}

// сохранить РМ mв файл с именемFNameв формате "РМ"

void SaveRM_dat(char FName[],TRow*&m)

{

FILE*f;

// открыть текстовый файл на чтение

f=fopen(FName,"w");

// Прохождение по всем Элементам всех Строк

TRow*r;

TEl*El;

r = m;

while (r != NULL)

{

El = r->El;

while (El != NULL)

{

fprintf(f,"%d %d %d\n",r->num,El->num,El->value);

El = El->next;

}

r = r->next;

}

// закрыть файл

fclose(f);

}

// копировать Строку r1 в Строкуr

void CopyRow(TRow*r1,TRow*&r)

{

TEl*El,*El1;

r->num = r1->num;

initEl(r->El);

El = r->El;

El1 = r1->El;

while (El1 != NULL)

{

El->num = El1->num;

El->value = El1->value;

El1 = El1->next;

if (El1 != NULL)

{

initEl(El->next);

El = El->next;

}

}

}

// возвращает 1, если (r1 !=NULL) И (r1->num < r2->num)

int LessRowNum(TRow*r1,TRow*r2)

{

if (r1 == NULL)

return 0;

else

return (r1->num < r2->num);

}

// Говоря "Строки r", будем подразумевать то, что указатель r может быть перемещен на r->next и далее.

// Аналогично будет использоваться выражение "Элементы El".

int ScrollRows(TRow*&r1,TRow*&r2,TRow*&r) // записывать Строки r1 в Строки r, пока LessRowNum(r1,r2)

// возвращаемое значение функции - 1, если хотя бы одна Строка из Строк r1 была записана в Строки r

{

int b=0;

while (LessRowNum(r1,r2))

{

b=1;

CopyRow(r1,r);

r1 = r1->next;

if (LessRowNum(r1,r2))

{

initRow(r->next);

r = r->next;

}

}

returnb;

}

// возвращает 1, если (El1 !=NULL) И (El1->num < El2->num)

int LessElNum(TEl*El1,TEl*El2)

{

if (El1 == NULL)

return 0;

else

return (El1->num < El2->num);

}

// записывать Элементы El1 вEl, покаLessElNum(El1,El2)

int ScrollEls(TEl*&El1,TEl*&El2,TEl*&El)

{

int b=0;

while (LessElNum(El1,El2))

{

b=1;

El->num = El1->num;

El->value = El1->value;

El1 = El1->next;

initEl(El->next);

El = El->next;

}

returnb;

}

// сложить Элементы El1 иEl2 и записать результат сложения вEl

void SumEls(TEl*El1,TEl*El2,TEl*&El)

{

El->value = El1->value + El2->value;

El->num = El1->num;

}

// записывать Элементы El1 вEl, покаEl1 !=NULL

int ScrollToEndEls(TEl*&El1,TEl*&El,int c)

{

int b = 0;

while (El1 != NULL)

{

b = 1;

if (c)

{

initEl(El->next);

El = El->next;

}

else

c = 0;

El->value = El1->value;

El->num = El1->num;

El1 = El1->next;

}

returnb;

}

// сложить две Строки r1 и r2 и записать результат сложения в r

void SumRows(TRow*r1,TRow*r2,TRow*&r)

{

TEl*El1,*El2,*El,*tmp;

El1 = r1->El;

El2 = r2->El;

r->num = r1->num;

initEl(El);

r->El = El;

int b;

do{

b= 0;

// записывать Элементы El1 вEl, покаLessElNum(El1,El2)

ScrollEls(El1,El2,El);

if (El1 != NULL)

// если номер столбца ЭлементаEl1 равен чем номер столбца ЭлементаEl2

if (El1->num == El2->num)

{ // то складываем их значения

b = 1;

SumEls(El1,El2,El);

El1 = El1->next;

El2 = El2->next;

}

// иначе меняем ролями El1 и El2

else

{

tmp = El1;

El1 = El2;

El2 = tmp;

}

else

// записывать Элементы El2 вEl, покаEl2 !=NULL

ScrollToEndEls(El2,El,b);

// если пока не все Элементы Строк r1 и Строк r2 обработаны

if (((El1 != NULL) || (El2 != NULL)) && b)

{

initEl(El->next);

El = El->next;

}

}while ((El1 != NULL) || (El2 != NULL));

}

// записывать в r1 Строкиr, покаr1 !=NULL

void ScrollToEndRows(TRow*&r1,TRow*&r)

{

while (r1 != NULL)

{

initRow(r->next);

r = r->next;

CopyRow(r1,r);

r1 = r1->next;

}

}

// == 1 <=> Строки r1 и Строкиr2 закончились

int End(TRow*r1,TRow*r2)

{

return ((r1 == NULL) && (r2 == NULL));

}

// если !End(r1,r2), то присоединить новую Строку к Строкамr

void ToNewRow(TRow*r1,TRow*r2,TRow*&r)

{

if (!End(r1,r2))

{

initRow(r->next);

r = r->next;

}

}

// сложить РМ m1 иm2, результат сложения - РМm

void Sum(TRow*m1,TRow*m2,TRow*&m)

{

// инициализировать первую Строку РМ m

initRow(m);

TRow*r,*r1,*r2,*tmp;

r1=m1;

r2=m2;

r=m;

do{

if (r2 != NULL)

{

// записывать Строки r1 в Строкиr, покаLessRowNum(r1,r2)

int b=ScrollRows(r1,r2,r);

// если хотя бы одна Строка записана в Строки r

if (b)

// если !End(r1,r2), то присоединить новую Строку к Строкам r

ToNewRow(r1,r2,r);

if (r1 != NULL)

// если номера строк r1 и r2 совпадают

if (r1->num == r2->num)

{// то складываем их, результат - Строка r

SumRows(r1,r2,r);

// перемещаемся на следующие Строки РМ m1 и m2

r1 = r1->next;

r2 = r2->next;

// если !End(r1,r2), то присоединить новую Строку к Строкам r

ToNewRow(r1,r2,r);

}

// иначе меняем Строки r1 и r2 ролями

else

{

tmp = r1;

r1 = r2;

r2 = tmp;

}

else

// записывать в r2 Строкиr, покаr2 !=NULL

ScrollToEndRows(r2,r);

}

else

// записывать в r1 Строкиr, покаr1 !=NULL

ScrollToEndRows(r1,r);

// пока не исчерпаны Строки РМ m1 и m2

}while (!End(r1,r2));

}

// умножить РМ m на скаляр

void Scalar(TRow*&m,float L)

{

TRow*r;

r=m;

TEl*El;

// прохождение по всей РМ и умножение каждого Элемента на L с округлением

while (r != NULL)

{

El = r->El;

while (El != NULL)

{

El->value = int(float(El->value)*L);

El = El->next;

}

r = r->next;

}

}

// вычесть из РМ m1 РМm2, результат - РМm

void Difference(TRow*m1,TRow*m2,TRow*&m)

{

// умножаем РМ m2 на -1, прибавляем её к РМ m1, опять умножаем m1 на -1

Scalar(m2,-1);

Sum(m1,m2,m);

Scalar(m2,-1);

}

// освобождение памяти

void FreeRM(TRow*&m)

{

TRow*r=m,*r1;

TEl*El,*El1;

while (r != NULL)

{

r1 = r->next;

El = r->El;

while (El != NULL)

{

El1 = El->next;

free(El);

El = El1;

}

free(r);

r = r1;

}

}

// поиск в РМ mстроки с номеромi

int SearchForRow(TRow*m, int i, TRow*&r, TRow*&rL)

{

r = m;

while ((r != NULL) && (r->num < i))

{

rL = r;

r = r->next;

}

if (r != NULL)

return (r->num == i);

else

return0;

}

// поиск среди Элементов ElЭлемента, стоящего вj-том столбце

int SearchForEl(TEl*El0, int j, TEl*&El, TEl*&ElL)

{

El = El0;

while ((El != NULL) && (El->num <j))

{

ElL = El;

El = El->next;

}

if (El != NULL)

return (El->num == j);

else

return0;

}

// ввод РМ с клавиатуры

void EnterRM(char s[])

{

int i,j,value;

FILE*f;

f=fopen(s,"w");

printf("When you want to finish, enter -1:\n");

int end = 0;

while (!end)

{

printf("i = ");

scanf("%d",&i);

if (i != -1)

{

printf("j = ");

scanf("%d",&j);

if (j != -1)

{

printf("value = ");

scanf("%d",&value);

fprintf(f,"%d %d %d\n",i,j,value);

}

else

end = 1;

}

else

end = 1;

}

printf("The matrix is saved to file \"%s\"\n",s);

fclose(f);

}

// добавить к имени матрицы расширение ".dat"

void MakeFName(char*&s)

{

int l = strlen(s);

s[l] = '.';

s[l+1] = 'd';

s[l+2] = 'a';

s[l+3] = 't';

s[l+4] = '\0';

}

// выделение памяти под строку из 100 символов

void initString(char*&s)

{

s = (char*)malloc(100*sizeof(char));

}

// копировать содержимое файла с именем s1 в файл с именем s0 посимвольно

void CopyFile(char*s1,char*s0)

{

FILE*f1,*f0;

f1 = fopen(s1,"r");

f0 = fopen(s0,"w");

char c;

while (fscanf(f1,"%c",&c) != EOF)

fprintf(f0,"%c",c);

fclose(f1);

fclose(f0);

}

// транспонировать РМ

void Transp_dat(char*s1,char*s0)

{

FILE*f1,*f0;

f1 = fopen(s1,"r");

f0 = fopen(s0,"w");

int i,j,value;

int first;

while (fscanf(f1,"%d %d %d",&i,&j,&value) != -1)

fprintf(f0,"%d %d %d\n",i,j,value);

fclose(f1);

fclose(f0);

}

// функция, с помощью которой осуществляется работа с меню

void MainMenu()

{

char c;

do{

printf("\n --- Rare matrices (RM) ---\n");

printf("What do you want to do?\n");

printf(" 1. Enter a new RM from keyboard.\n");

printf(" 2. Add one RM to another.\n");

printf(" 3. Transp.\n");

printf(" 4. Multiply on scalar.\n");

printf(" 5. Convert txt-file to dat-file.\n");

printf(" 6. Convert dat-file to txt-file.\n");

printf(" 7. Copy one RM to another.\n");

printf(" 0. Exit program.\n");

c=getch();

switch (c)

{

case '1': {

printf("Enter name of matrix:\n");

printf("(if a matrix with the same name already exists, it will be lost)\n");

char*s;

// выделение памяти под строковую переменную

initString(s);

// ввод имени РМ с клавиатуры

scanf("%s",s);

// добавление расширения ".dat"

MakeFName(s);

// ввод РМ с клавиатуры

EnterRM(s);

break;

}

case '2': {

printf("Enter names of matrixes in expression:\n");

printf("M0 := M1 + M2\n");

TRow*m1,*m2,*m0;

char*s1,*s2,*s0;

initString(s1); // выделение памяти под строковые переменные

initString(s2);

initString(s0);

printf(" M1: ");

scanf("%s",s1);

printf(" M2: ");

scanf("%s",s2);

printf(" M0: ");

scanf("%s",s0);

// добавление к имени каждой матрицы расширения ".dat"

MakeFName(s1);

MakeFName(s2);

MakeFName(s0);

// загрузка РМ m1 иm2 из файлов

LoadRM_dat(s1,m1);

LoadRM_dat(s2,m2);

// сложение РМ m1 и m2, результат - РМ m0

Sum(m1,m2,m0);

// запись РМ m0 в файл

SaveRM_dat(s0,m0);

// освобождение памяти

FreeRM(m1);

FreeRM(m2);

FreeRM(m0);

break;

}

case '3': {

printf("Enter names of matrices M1 and M2:\n")

printf("M0 := (M1)^T\n");

char*s1,*s0;

// выделение памяти под строковые переменные

initString(s1);

initString(s0);

printf(" M1: ");

scanf("%s",s1);

// добавление расширения ".dat"

MakeFName(s1);

printf(" M0: ");

scanf("%s",s0);

// добавление расширения ".dat"

MakeFName(s0);

// транспонирование

Transp_dat(s1,s0);

break;

}

case '4': {

printf("Enter name of matrix: ");

char*s;

TRow*m;

// выделение памяти под строковую переменную

initString(s);

scanf("%s",s);

// добавление расширения ".dat"

MakeFName(s);

float k;

printf("Enter scalar: ");

scanf("%f",&k);

// загрузка РМ из файла

LoadRM_dat(s,m);

// умножение РМ на скаляр

Scalar(m,k);

// запись РМ в файл

SaveRM_dat(s,m);

// освобождение памяти

FreeRM(m);

break;

}

case '5': {

printf("Enter name of matrix: ");

char*s;

initString(s);

scanf("%s",s);

int l = strlen(s);

// добавление расширения ".txt"

s[l] = '.';

s[l+1] = 't';

s[l+2] = 'x';

s[l+3] = 't';

s[l+4] = '\0';

TRow*m;

// загрузка из файла с расширением ".txt"

LoadRM_txt(s,m);

s[l+1] = 'd';

s[l+2] = 'a';

s[l+3] = 't';

// сохранение в файл с расширением ".dat"

SaveRM_dat(s,m);

break;

}

case '6': {

printf("Enter name of matrix: ");

char*s;

// выделение памяти под строковую переменную

initString(s);

scanf("%s",s);

int l = strlen(s);

// добавление расширения ".dat"

MakeFName(s);

TRow*m;

// загрузка из файла с расширением ".dat"

LoadRM_dat(s,m);

s[l+1] = 't';

s[l+2] = 'x';

s[l+3] = 't';

// сохранение в файл с расширением ".txt"

SaveRM_txt(s,m);

break;

}

case '7': {

char*s1,*s0;

initString(s1);

initString(s0);

printf("Source: ");

scanf("%s",s1);

printf("Destination: ");

scanf("%s",s0);

// добавление расширения ".dat"

MakeFName(s1);

MakeFName(s0);

// копировать содержимое файла с именем s1 в файл с именем s0 посимвольно

CopyFile(s1,s0);

break;

}

}

}while (c != '0');

}

int main()

{

MainMenu();

return 0;

}

Лабораторная работа №19

Бинарные деревья

Цель: закрепление на практике знаний работы алгоритмов с бинарными деревьями. Построение бинарного упорядоченного дерева. Вывод вершин дерева на экран. Удаление и вставка вершины.

Методические рекомендации: лабораторная работа рассчитана на 2 часа и состоит из анализа одного разобранного задания и самостоятельной работы.

Для работы с деревьями предусмотрено 4 лабораторные работы и одно обязательное зачетное задание.

Необходимый уровень знаний:

  • работа с указателями;

  • работа со структурами;

  • механизм рекурсии.

Задача

Построить упорядоченное бинарное дерево. Написать функции, которые

реализуют следующие возможности:

- вывод вершин дерева на экран;

- вставка вершины;

- удаление вершины.

Дополнительное условие: данные для построения дерева должны по желанию пользователя вводиться с клавиатуры или считываться из файла.

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

#include <stdio.h>

#include <stdlib.h>

// вершина

typedef struct v_

{

// число в данной вершине

int x;

// указатели на: левого потомка, правого потомка, родителя

struct v_*L;

struct v_*R;

struct v_*P;

} v;

// корень дерева

v*root;

// количество элементов в массиве

int n;

// массив

int*a = 0;

// вспомогательный счётчик для печати дерева по уровням

int hcounter;

// инициализация новой вершины

void newv(v*&V)

{

// выделение памяти

V= (v*)malloc(sizeof(v));

// обнуление всех указателей и числа в вершине

V->L = 0;

V->R = 0;

V->x = 0;

V->P = 0;

}

// печать вершин дерева

void Print(v*V)

{

if (V)

{

Print(V->L);

printf("%d ",V->x);

Print(V->R);

}

}

// дополнительная функция вывода пробелов для вывода вершин дерева по уровням

void printspaces(int h) //h – номер уровня

{

if (h)

printf(" ");

for (int i=0; i<h-1; i++)

printf(" ");

}

// печать вершин дерева по уровням

void printtree(v*V, int h, int lr)

{

if (V->R)

{

// рекурсивный вызов функции printtree() для правого потомка

printtree(V->R,h+1,1);

}

printspaces(h);

if (lr)

if (lr == 1)

printf("/->");

else

printf("\\->");

printf("%d",V->x);

printf("\n");

if (V->L)

{

// рекурсивный вызов функции printtree() для правого потомка

printtree(V->L,h+1,2);

}

}

// ввод массива с клавиатуры

void InputKeyboard()

{

printf("n = ");

scanf("%d",&n);

a = (int*)malloc(sizeof(int)*n);

for (int i=0; i<n; i++)

scanf("%d",&a[i]);

}

// ввод массива из файла

void InputFile()

{

FILE*f;

f = fopen("bintree.txt","r");

// если файл успешно открыт на чтение

if (f)

{

fscanf(f,"%d",&n);

a = (int*)malloc(sizeof(int)*n);

for (int i=0; i<n; i++)

fscanf(f,"%d",&a[i]);

fclose(f);

}

else

printf("File \"bintree.txt\" does not exist!\n");

}

// дополнительная функция для вставки новой вершины

// нахождения места для числа x в дереве

v*findv(v*V,int x)

{

if (V->x > x)

if (V->L)

return findv(V->L,x);

else

{

newv(V->L);

V->L->P = V;

return V->L;

}

else

if (V->R)

return findv(V->R,x);

else

{

newv(V->R);

V->R->P = V;

return V->R;

}

}

// добавить в дерево вершину с числом x

void addv(v*root,int x)

{

findv(root,x)->x = x;

}

// сформировать дерево по массиву

void FormBT()

{

newv(root);

root->x = a[0];

for (int i=1; i<n; i++)

addv(root,a[i]);

}

// ввывод массив на экран

void printarray()

{

for (int i=0; i<n; i++)

printf("%d ",a[i]);

printf("\n");

}

// перейти по коду вершины к указателю на неё

int CodeToV(char*code,v*&cur)

{

cur = root;

int correct = 1;

int i = 0;

while (code[i] && correct)

{

switch (code[i])

{

case 'L': if (cur->L) cur = cur->L; else correct = 0; break;

case 'R': if (cur->R) cur = cur->R; else correct = 0; break;

default: correct = 0;

}

i++;

}

returncorrect;

}

// печать первым способом

void Print1()

{

Print(root);

}

// печать вторым способом

void Print2()

{

printtree(root,0,0);

}

v*&child(v*P,v*V)

// в зависимости от того, левым или правым потомком приходится V родителю P,

// возвращается ссылка на указатель соответствующую переменную

{

/ если V является левым потомком вершины P

if (P->L == V)

// то вернуть ссылку на переменнуюP->L

return P->L;

else

// иначе вернуть ссылку на переменную P->R

return P->R;

}

// удалить вершину V

void delv(v*&V)

{

// если есть и левый, и правый потомки

if (V->L && V->R)

{

// находим самый правый лист поддерева, корнем которого является V->L

v*U = V->L;

while (U->R)

U = U->R;

// ставим его вместо V

V->x = U->x;

// на месте листа теперь ничего нет

U->P->R = 0;

// освобождаем память

free(U);

}

else

{

v*U;

// если у V есть левый потомок

if (V->L)

U = V->L;

else

U = V->R;

if (U)

// родитель V становится родителем U

U->P = V->P;

// U - потомок родителя V становится потомком родителя V->P

child(V->P,V) = U;

// освобождение памяти

free(V);

}

}

// печать одно уровня

void printlevel(v*V,int h,int curh)

{

if (curh == h)

{

printf("%d ",V->x);

hcounter++;

}

else

// если нужная глубина hещё не достигнута

if (curh < h)

{

// рекурсивные вызовы printlevel() для левого и правого потомков

// (если таковые имеются)

if (V->L) printlevel(V->L,h,curh+1);

if (V->R) printlevel(V->R,h,curh+1);

}

}

// печать дерева по уровням

void PrintLevels()

{

hcounter = 1;

inth= 0;

// пока на уровне h есть хотя бы одна вершина

while (hcounter)

{

hcounter = 0;

printlevel(root,h,0);

printf("\n");

// увеличение глубины на 1

h++;

}

}

void Menu()

{

int c;

do{

printf("\n Work with a binary tree (BT)\n");

printf("\nYou can...\n\n");

printf("1. Enter an array from keyboard.\n");

printf("2. Load an array from file.\n");

printf("3. Print the array.\n");

printf("4. Form the BT by array.\n");

printf("5. Print the BT (1).\n");

printf("6. Print the BT (2).\n");

printf("7. Print levels of BT.\n");

printf("8. Add a vertex.\n");

printf("9. Delete a vertex.\n");

printf("0. Exit the program.\n");

printf("\nType 0-5 and press \"Enter\" ");

scanf("%d",&c);

switch (c)

{

case 1: InputKeyboard(); break;

case 2: InputFile(); break;

case 3: printarray(); break;

case 4: if (a) FormBT(); else printf("You have no array yet!\n"); break;

case 5: Print1(); break;//printtree(root,0); break;

case 6: Print2(); break;

case 7: PrintLevels(); break;

case 8: {

printf("x = ");

int x;

scanf("%d",&x);

addv(root,x);

Print2();

break;

}

case 9: {

char code[255];

printf("Enter a code of vertex (use \'L\' and \'R\'): ");

scanf("%s",code);

v*cur;

if (!CodeToV(code,cur))

printf("The code is incorrect!\n");

else

delv(cur);

Print2();

break;

}

}

printf("OK\n");

}while (c != 0);

}

int main()

{

Menu();

return 0;

}

Самостоятельная работа

Поменять местами правое и левое поддерево.

Лабораторная работа №20

Бинарные деревья

Цель: закрепление на практике знания работы алгоритмов с бинарными деревьями:

  • балансировка дерева;

  • реконструкция дерева таким образом, чтобы заданная вершина стала корнем.

Методические рекомендации: лабораторная работа рассчитана на 2 часа и состоит из анализа одного разобранного задания.

Самостоятельная работа не предусмотрена.

Обязательное зачетное задание.

Необходимый уровень знаний:

  • работа с указателями;

  • работа со структурами;

  • механизм рекурсии.

Задача

Построить упорядоченное бинарное дерево. Написать функции, которые

реализуют следующие возможности:

- вывод вершин дерева на экран;

- сделать заданную вершину корнем (с сохранением упорядоченности);

- сбалансировать дерево.

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

#include <stdio.h>

#include <stdlib.h>

// вершина

typedef struct v_

{

// число в данной вершине

int x;

// указатели на: левого потомка, правого потомка, родителя

struct v_*L;

struct v_*R;

struct v_*P;

int rightkid;

} v;

// корень дерева

v*root;

// количество элементов в массиве

int n;

// массив

int*a = 0;

inthcounter;

// инициализация новой вершины

void newv(v*&V)

{

// выделение памяти

V = (v*)malloc(sizeof(v));

// обнуление всех указателей и числа в вершине

V->L = 0;

V->R = 0;

V->x = 0;

V->P = 0;

V->rightkid= 0;

}

// дополнительная функция вывода пробелов для вывода вершин дерева по уровням

void printspaces(int h) //h – номер уровня

{

if (h)

printf(" ");

for (int i=0; i<h-1; i++)

printf(" ");

}

// печать дерева вторым способом

void printtree(v*V,int h,int lr)

{

if (V->R)

// рекурсивный вызов функции printtree() для правого потомка

printtree(V->R,h+1,1);

printspaces(h);

if (lr)

if (lr == 1)

printf("/->");

else

printf("\\->");

printf("%d",V->x);

printf("\n");

if (V->L)

// рекурсивный вызов функции printtree() для правого потомка

printtree(V->L,h+1,2);

}

// ввод массива с клавиатуры

void InputKeyboard()

{

printf("n = ");

scanf("%d",&n);

a = (int*)malloc(sizeof(int)*n);

for (int i=0; i<n; i++)

scanf("%d",&a[i]);

}

// ввод массива из файла

void InputFile()

{

FILE*f;

f = fopen("bintree.txt","r");

fscanf(f,"%d",&n);

a = (int*)malloc(sizeof(int)*n);

for (int i=0; i<n; i++)

fscanf(f,"%d",&a[i]);

fclose(f);

}

// найти место для числа x в дереве

v*findv(v*V,int x)

{

if (V->x > x)

if (V->L)

return findv(V->L,x);

else

{

newv(V->L);

V->L->P = V;

V->L->rightkid = 0;

return V->L;

}

else

if (V->R)

return findv(V->R,x);

else

{

newv(V->R);

V->R->P = V;

V->R->rightkid = 1;

return V->R;

}

}

// добавить в дерево вершину с числом x

void addv(v*root,int x)

{

findv(root,x)->x = x;

}

// сформировать дерево по массиву

void FormBT()

{

newv(root);

root->x = a[0];

for (int i=1; i<n; i++)

addv(root,a[i]);

}

// распечатать массив на экран

void printarray()

{

for (int i=0; i<n; i++)

printf("%d ",a[i]);

printf("\n");

}

// перейти по коду вершины к указателю на неё

int CodeToV(char*code,v*&cur)

{

cur = root;

int correct = 1;

int i = 0;

while (code[i] && correct)

{

switch (code[i])

{

case 'L': if (cur->L) cur = cur->L; else correct = 0; break;

case 'R': if (cur->R) cur = cur->R; else correct = 0; break;

default: correct = 0;

}

i++;

}

returncorrect;

}

// спуститься вниз влево по дереву до листа

void goleft(v*from,v*&to)

{

to = from;

while (to->L)

to = to->L;

}

// спуститься вниз вправо по дереву до листа

void goright(v*from,v*&to)

{

to = from;

while (to->R)

to = to->R;

}

int up(v*&U,int rightkid)

{

if (U->P)

if (rightkid)

if (U->P->R == U)

{

U = U->P;

return 1;

}

else

return 0;

else

if (U->P->L == U)

{

U = U->P;

return 1;

}

else

return 0;

else

return 0;

}

// сделать вершину V корнем (с сохранением упорядоченности!)

void VToRoot(v*&V)

{

v*Lcorner,*Rcorner;

// идём от V влево вниз до упора, результат сохраняется в Lcorner

goleft(V,Lcorner);

// идём от V вправо вниз до упора, результат сохраняется в Rcorner

goright(V,Rcorner);

// (rightkid == 1) <=> V - правый потомок своего родителя

int rightkid = V->rightkid;

// теперь V - корень

root = V;

// будем двигаться от родителя вершины V вверх, присоединяя отдельные части

// дерева вниз (к веткам, отходящим от V)

v*U = V->P;

// теперь у V нет родителя

V->P = 0;

v*U1;

// пока не достигнут бывший корень дерева

while (U != 0)

{

U1 = U;

// поднимаемся вверх по дереву, пока значение U1->rightkid не изменится на

// противоположное

while (up(U1,rightkid));

// присоединяем часть дерева (либо к Lcorner, либо к Rcorner в зависимости

// от rightkid)

if (rightkid)

{

U->R = 0;

U = U1->P;

Lcorner->L = U1;

U1->rightkid = 0;

U1->P = Lcorner;

goleft(Lcorner,Lcorner);

}

else

{

U->L = 0;

U = U1->P;

Rcorner->R = U1;

U1->rightkid = 1;

U1->P = Rcorner;

goright(Rcorner,Rcorner);

}

rightkid= !rightkid;

}

}

// добавить в массив поддерево с корнем V

void ToArray(v*V)

{

if (V->L) ToArray(V->L);

a[n] = V->x; n++;

if (V->R) ToArray(V->R);

}

// сформировать упорядоченный массив по упорядоченному бинарному дереву

void FormArrayByBT()

{

n = 0;

ToArray(root);

}

// занести в сбалансированное дерево числа из массива с номерами от i1 до i2

// включительно

void Balanced(int i1,int i2,v*&V)

{

newv(V);

int i = (i1 + i2)/2;

V->x = a[i];

if (i1 <= i-1)

Balanced(i1,i-1,V->L);

if (i2 >= i+1)

Balanced(i+1,i2,V->R);

}

// сформировать сбалансированное дерево по упорядоченному массиву

void FormBalancedBTByArray()

{

Balanced(0,n-1,root);

}

// сбалансировать дерево

void Balance2()

{

// сформировать упорядоченный массив по упорядоченному бинарному дереву

FormArrayByBT();

// сформировать сбалансированное дерево по упорядоченному массиву

FormBalancedBTByArray();

}

void Menu()

{

int c;

do{

printf("\n Work with a binary tree (BT)\n");

printf("\nYou can...\n\n");

printf("1. Enter an array from keyboard.\n");

printf("2. Load an array from file.\n");

printf("3. Print the array.\n");

printf("4. Form the BT by array.\n");

printf("5. Print the BT.\n");

printf("6. One of vertices -> root.\n");

printf("7. Balance the BT.\n");

printf("0. Exit the program.\n");

printf("\nType 0-5 and press \"Enter\" ");

scanf("%d",&c);

switch (c)

{

case 1: InputKeyboard(); break;

case 2: InputFile(); break;

case 3: printarray(); break;

case 4: if (a) FormBT(); else printf("You have no array yet!\n"); break;

case 5: printtree(root,0,0); break;

case 6: {

char code[255];

printf("Enter a code of vertex (use \'L\' and \'R\'): ");

scanf("%s",code);

v*cur;

if (!CodeToV(code,cur))

printf("The code is incorrect!\n");

else

/*

{

root = cur;

VToRoot(cur,0);

}

*/

VToRoot(cur);

//VToRoot2(cur,root,code[0]);

break;

}

case 7: Balance2(); break;

}

printf("OK\n");

}while (c != 0);

}

int main()

{

Menu();

return 0;

}

Лабораторная работа №21

Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]