- •Лабораторные работы. Сборник задач.
- •Оглавление
- •Часть 1. Лаборатоные работы
- •Работа со структурами и объединениями …………………………………….91
- •3 Задача
- •4 Задача
- •5 Задача
- •6 Задача
- •Дополнительное условие:использование цикла с предусловием.
- •1 Задача
- •2 Задача
- •Дополнительное условие: программа написана без использования функции.
- •Дополнительное условие: программа написана с использованием функций.
- •3 Задача
- •Дополнительное условие: программа написана без использования функции.
- •Дополнительное условие: программа написана с использованием функции.
- •4 Задача
- •Дополнительное условие: программа написана без использования функции
- •Дополнительное условие: программа написана с использованием функции.
- •Самостоятельная работа
- •Лабораторная работа №3
- •Самостоятельная работа
- •1 Задача
- •2 Задача
- •1 Задача
- •2 Задача
- •3 Задача
- •Лабораторная работа №6
- •1 Задача
- •1 Задача
- •2 Задача
- •3 Задача
- •1 Задача
- •1 Задача
- •2 Задача
- •3 Задача
- •4 Задача
- •Синтаксический анализатор
- •Самостоятельная работа
- •1 Задача
- •2 Задача
- •3 Задача
- •Работа с каталогами
- •Самостоятельная работа
- •1 Задача
- •2 Задача
- •1 Задача
- •2 Задача
- •3 Задача
- •1 Задача
- •2 Задача
- •1 Задача Реализовать очередь, состоящую из целых чисел
- •Комментарий:
- •2 Задача
- •1 Задача
- •Идеально-сбалансированные деревья
- •1 Задача
- •2 Задача
- •1 Задача
- •2 Задача
- •3 Задача
- •1 Задача
- •2 Задача
- •3 Задача
- •1 Задача
- •1 Задача
- •1 Уровень сложности
- •2 Уровень сложности
- •3 Уровень сложности
- •1 Уровень сложности.
- •2 Уровень сложности
- •3 Уровень сложности
- •1 Уровень сложности
- •Работа с несколькими массивами
- •Преобразование массива
- •Изменение элементов массива
- •2 Уровень сложности Формирование массива и вывод его элементов
- •Анализ элементов массива
- •Преобразование массива
- •Изменение элементов массива
- •Удаление и вставка элементов
- •Серии целых чисел
- •3 Уровень сложности Множества точек на плоскости
- •1 Уровень сложности
- •2 Уровень сложности
- •3 Уровень сложности
- •1 Уровень сложности
- •2 Уровень сложности
- •3 Уровень сложности
- •1 Уровень сложности
- •2 Уровень сложности
- •3 Уровень сложности
- •Not простое_логическое
- •(Простое_логическое знак_операции простое_логическое)
- •Построить синтаксический анализатор для понятия предложение.
- •1 Уровень сложности
- •2 Уровень сложности
- •1 Уровень сложности
- •Примеры:
- •Двусвязные списки
- •1 Уровень сложности
- •2 Уровень сложности
- •3 Уровень сложности
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