Консультация № 186094
18.05.2012, 22:53
240.00 руб.
0 9 1
Уважаемые эксперты! Прошу вас ответить на следующий вопрос и прокоментировать код:
Написать программу для работы по запросам оператором с упорядочнной таблицей, реализованной в виде двоичного дерева поиска.
Ключ - целое число. Информация - строка произвольной длины. Узел дерева содержит ключ, указатели на правое и левое поддеревья и указатель на информационное поле.
В таблице не могут храниться записи с одинаковыми ключами.
Предусмотреть следующие операции:
- включение нового элемента в таблицу без нарушения свойств упорядочнности; если информация с заданным ключом уже есть, то выводится сообщение об ошибке;
- удаление из таблицы элемента, заданного своим ключом, без нарушения свойств упорядочности таблицы;
- поиск информации по заданному ключу;
- вывод всего содержимого таблицы в обратном порядке следования ключей;
- вывод всего содержимого таблицы в порядке добавления элементов в таблицу.

Примечания:
1. Программа должна содержать несколько функций; функция main должна выполнять: вывод меню, ввод и анализ ответа, вызов на исполнение требуемой функции.
2. В программе нужно предусмотреть проверку правильности ввода данных.
3. Для целей отладки реализовать формированный вывод таблицы в виде дерева.
4.Для целей отладки реализовать загрузку таблицы из файла в формате:
- ключ1
-Информация1
-ключ2
-...
5.Провести таймирование( или профилирование) программы.

Обсуждение

давно
Посетитель
7438
7205
19.05.2012, 20:32
общий
Что именно понимается под "таймированием( или профилированием) программы"?
Сколько времени занимает та или иная операция?
Об авторе:
"Если вы заметили, что вы на стороне большинства, —
это верный признак того, что пора меняться." Марк Твен
Неизвестный
19.05.2012, 20:45
общий
19.05.2012, 21:26
Адресаты:
Да сколько занимает по времени, извините но 5-ый пункт не нужен.
давно
Профессор
399103
482
20.05.2012, 00:45
общий
это ответ
Здравствуйте, ШмонинЕА!

[code h=200]
// майкросовтовский компилятор без этой строчки выдаёт предупреждения на стандартные POSIX-овские функции вида scanf и т.п.
#define _CRT_SECURE_NO_WARNINGS

#include <stdio.h>
#include <string.h>
#include <stdlib.h>

#include <locale.h>

// тип "узел"
typedef
struct _tagNode
{
int key;
char * data;
_tagNode * left;
_tagNode * right;
}
Node;

// создаёт пустой узел
Node * assignNode()
{
Node * node;

node = (Node *)malloc( sizeof(Node) );
//node->key = 0;
node->data = 0;
node->left = 0;
node->right = 0;

return node;
}

// освобождает память ноды и всех детей
void freeNode( Node * node )
{
if( node == 0 ) return;

freeNode( node->left );
freeNode( node->right );

if( node->data != 0 ) free( node->data );

free( node );
}

// освобождает память детей, а саму зануляет
void nullNode( Node * node )
{
if( node == 0 ) return;

freeNode( node->left );
freeNode( node->right );

if( node->data != 0 ) free( node->data );

//node->key = 0;
node->data = 0;
node->left = 0;
node->right = 0;
}

void copyNode( const Node * from, Node * to )
{
to->key = from->key;
to->data = from->data;
to->left = from->left;
to->right = from->right;
}

// добавление элемента
void insert( Node * root, int key, const char * data )
{
// если дерево пустое
if( root->data == 0 )
{
// без затей заполняем поля
// выделяем память(+1 - символ завершения строки)
root->data = (char *)malloc( (strlen(data) + 1) * sizeof(char) );
root->key = key;
strcpy( root->data, data );
}
else // если нет
{
// рекурсивно добавляем в правое или левое поддерево, в зависимости от ключа
int cmp = (key == root->key ? 0 : (key > root->key ? 1 : -1));//strcmp( key, root->key );
if( cmp != 0 )
{
Node * child = 0;

if( cmp > 0 )
child = root->right;
else
child = root->left;

// если узел пустой
if( child == 0 )
{
// создаём его
child = assignNode();

// и назначаем правым
if( cmp > 0 )
root->right = child;
// или левым ребёнком
else
root->left = child;
}

insert( child, key, data );
}
else
{
printf( "Ошибка: элемент с ключом %i уже существует\n", key );
}
}
}

// удаление узла
void remove( Node * root, int key )
{
// если дерево пустое
if( root == 0 ? true : root->data == 0 ) return;

int cmp = (key == root->key ? 0 : (key > root->key ? 1 : -1));//strcmp( key, root->key );
if( cmp > 0 )
remove( root->right, key );
else if( cmp < 0 )
remove( root->left, key );
else
{
// если обоих детей нет
if( root->left == 0 && root->right == 0 )
{
// зануляем текущий узел
nullNode( root );
}
// если есть только левый
else if( root->left != 0 && root->right == 0 )
{
Node * child = root->left;
copyNode( child, root );
freeNode( child );
}
// если есть только правый
else if( root->left == 0 && root->right != 0 )
{
Node * child = root->right;
copyNode( child, root );
freeNode( child );
}
// если же оба есть
else
{
// ищем в правом поддереве самый левый узел
Node * curNode = root->right;
Node * needNode = curNode;
while( curNode != 0 )
{
if( curNode->data != 0 )
needNode = curNode;
curNode = curNode->left;
}
// левый узел "самого левого узла" может оказаться пустым. освободим память
freeNode( curNode->left );

// копируем данные
root->key = curNode->key;
root->data = curNode->data;

// рекурсивно удаляем узел
remove( curNode, curNode->key );
}
}
}

// поиск элемента
Node * find( Node * root, int key )
{
// дерево пустое - ничего не нашли
if( root == 0 ? true : root->data == 0 ) return 0;

int cmp = (key == root->key ? 0 : (key > root->key ? 1 : -1));//strcmp( key, root->key );
if( cmp < 0 )
// рекурсивно ищем в левом поддереве
return find( root->left, key );
else if( cmp > 0 )
// в правом
return find( root->right, key );
// нашли!!!
return root;
}

void print_cool( Node * root, unsigned n = 0 )
{
// если дерево пустое - выводим символ пустого узла и всё
if( root == 0 ? true : root->key == 0 )
{
for( unsigned i = 0; i < n; ++i )
printf( " " );
printf( "-\n" );
return;
}

// выводим себя
for( unsigned i = 0; i < n; ++i )
printf( " " );
printf( "%s\n", root->data );

// рекурсивно выводим левое поддерево
print_cool( root->left, n + 1 );

// правое поддерево
print_cool( root->right, n + 1 );
}

// выводит элементы дерева, ключ которых превышает данный, в порядке убывания ключей
void print( Node * root )
{
// если дерево пустое - выводим символ пустого узла и всё
if( root == 0 ? true : root->data == 0 )
{
return;
}

// правое поддерево
print( root->right );

// выводим себя
printf( "%s\n", root->data );

// рекурсивно выводим левое поддерево
print( root->left );
}

int load( const char * fileName, Node * root )
{
FILE * file;

char str[64];
int i;

file = fopen( fileName, "r" );

if( 0 == file)
{
printf( "Не получилось открыть файл '%s'\n", fileName );
return 0;
}

while( 2 == fscanf( file, "%i%s", &i, str ) )
{
insert( root, i, str );
}

fclose( file );

return 1;
}

int main( int argc, char *argv[] )
{
// для нормального отображения кириллицы
setlocale( LC_ALL, "Russian" );

//load( "zzz.txt" );

// хотя бы корень надо создать руками
Node * root = assignNode();

// тестовое содержание дерева. не обязательно
insert( root, 2, "bbb" );
insert( root, 3, "ccc" );
insert( root, 1, "aaa" );
insert( root, 4, "ddd" );
insert( root, 5, "aaaa" );

// буферы для ввода
const int BUF_SIZE = 64;
char buf[BUF_SIZE];
char buf1[BUF_SIZE];
char buf2[BUF_SIZE];
int i;

// консольные команды
const char com_insert [] = "insert";
const char com_remove [] = "remove";
const char com_find [] = "find";
const char com_print [] = "print";
const char com_print_c [] = "print_cool";
const char com_load [] = "load";
const char com_help [] = "help";
const char com_exit [] = "exit";

printf( "Введите help для вывода списка команд\n" );

do
{
scanf( "%s", buf );
if( 0 == strcmp( buf, com_insert ) )
{
printf( "Введите ключ: " );
scanf( "%i", &i );
printf( "Введите данные: " );
scanf( "%s", buf2 );

insert( root, i, buf2 );
}
if( 0 == strcmp( buf, com_load ) )
{
printf( "Введите имя файла: " );
scanf( "%s", buf2 );

load( buf2, root );
}
else if( 0 == strcmp( buf, com_remove ) )
{
printf( "Введите ключ: " );
scanf( "%i", &i );

remove( root, i );
}
else if( 0 == strcmp( buf, com_find ) )
{
printf( "Введите ключ: " );
scanf( "%i", &i );

Node * node = find( root, i );
if( node == 0 )
printf( "Таких элементов не найдено\n" );
else
printf( "Данные: %s\n", node->data );
}
else if( 0 == strcmp( buf, com_print ) )
{
print( root );
}
else if( 0 == strcmp( buf, com_print_c ) )
{
print_cool( root );
}
else if( 0 == strcmp( buf, com_help ) )
{
printf( "Команды:\n" );
printf( "%s - добавление элемента\n", com_insert );
printf( "%s - удаление элемента\n", com_remove );
printf( "%s - поиск элемента с данным ключом\n", com_find );
printf( "%s - вывод элементов в порядке убывания улючей\n", com_print );
printf( "%s - вывод дерева в читаемом виде\n", com_print_c );
printf( "%s - загрузка из файла\n", com_load );
printf( "%s - выход из программы\n", com_exit );
}
}
while( 0 != strcmp(buf, com_exit) );

// освобождаем память. хоть её всё равно ОС освободит...
freeNode( root );

return 0;
}
[/code]
Неизвестный
20.05.2012, 13:15
общий
20.05.2012, 15:01
Александр, вопрос к вам у вас при вводе ключа одного и того же два раз должно выдавать ошибку,а программа вылетает.У вас вылетала программа или нет?
Еще вопрос с открытием файла, что должно лежать в файле и какого расширения должен быть файл?
Можете подправить пожалуйста дополнительные обработчики исключения:
если информация с заданным ключом уже есть, то выводится сообщение об ошибке;
давно
Профессор
399103
482
20.05.2012, 21:37
общий
Цитата: 393171
при вводе ключа одного и того же два раз должно выдавать ошибку,а программа вылетает

Да, действительно. Поправил.

Цитата: 393171
что должно лежать в файле и какого расширения должен быть файл?

Как и указано в задании - пары строк вида
'ключ'
'строка'
Т.е. пример валидного файла:
Код:

42
ololo
314
zzz
.

Расширение может быть любым - это часть имени файла.
Неизвестный
21.05.2012, 15:11
общий
21.05.2012, 15:14
Еще Александр проверьте на тестовом дереве команду find, она почему то находит только ключ 2, почему остальные ключи не находит?
давно
Профессор
399103
482
21.05.2012, 15:46
общий
Угу, поправил. Там < с > были перепутаны.
Неизвестный
21.05.2012, 19:38
общий
22.05.2012, 00:11
И еще вопрос Александр проверьте пожалуйста дерево выводится правильно?
И еще пожалуйста измените меню чтобы не полностью вводить его а цифрами от 1 до 8
давно
Профессор
399103
482
22.05.2012, 00:19
общий
22.05.2012, 00:26
Цитата: 393171
И еще вопрос Александр проверьте пожалуйста дерево выводится правильно?

Вроде да. Можно пример подозрительного дерева?

Цитата: 393171
И еще пожалуйста измените меню чтобы не полностью вводить его а цифрами от 1 до 8

Для этого просто замените константы консольных команд на 1, 2, 3, ...
Форма ответа