Консультация # 186234: Уважаемые эксперты! Пожалуйста, ответьте на вопрос: Написать программу для работы с перемешанной таблицей, использующей перемешивание сцеплением, по запросам оператора. В основной области перемешанной таблицы находится указатель на элементы из области переполнения; каждый элемент области переполнения имеет следующую структуру: struct Ofl {...
Уважаемые эксперты! Пожалуйста, ответьте на вопрос: Написать программу для работы с перемешанной таблицей, использующей перемешивание сцеплением, по запросам оператора. В основной области перемешанной таблицы находится указатель на элементы из области переполнения; каждый элемент области переполнения имеет следующую структуру: struct Ofl { int key; /*ключ элемента*/ char *info; /*указатель на информацию */ Ofl *next; /*указатель на следующий элемент*/ }; Максимальный
размер основной области ограничен (для задания максимального размера использовать константу -например, const int SIZE = ...;).
Предусмотреть следующие операции: -включение нового элемента в таблицу при условии, что в таблице не может быть двух элементов с одинаковыми ключами; если при включении нового элемента возникает такая ситуация, на экран должно быть выведено сообщение об ошибке; -удаление элемента из таблицы по заданному ключу. Если запрошенный элемент
в таблице отсутствует, вывести на экран сообщение об ошибке; - вывод содержимого таблицы на экран.
Разработать два варианта программы: а) и сама таблица, и информация, относящаяся к элементу таблицы, хранятся в основной памяти. b) сама таблица хранится в основной памяти, а информация, относящаяся к элементу таблицы, хранится во внешней памяти (используется двоичный файл произвольного доступа), причем она записывается в файл сразу же при выполнении операции включения в таблицу. Имя файла вводится
по запросу из программы. По завершении работы элементы таблицы записываются во внешнюю память, а в начале нового сеанса таблица строится на основе информации во внешней памяти.
ПРИМЕЧАНИЕ: 1. Программа должна содержать несколько функций; функция main должна выполнять: вывод меню, ввод и анализ ответа, вызов на исполнение требуемой функции; 2. в программе нужно предусмотреть проверку правильности ввода данных; 3. Для варианта b) следует модифицирова
ть структуру, определяющую элемент области переполнения, включив в нее длину информации и ее смещение в файле; 4. В варианте b) для работы с файлом использовать функции проекта stdio.h; чтение и запись выполнять с помощью fread() и fwrite(), в которых должна быть указана реальная длина информации.
Здравствуйте, Орт Кирилл Валерьевич! Вот Вам программы на перемешивание сцеплением а)
Код :
#include <locale>
#include <iostream>
using namespace std;
void AddElement(void);
void DelElement(void);
void OutTable(void);
void Exit(void);
int GetNum(void);
const int SIZE = 10;
typedef struct Ofl
{
int key; //ключ элемента
char *info; //указатель на информацию
Ofl *next; //указатель на след. элемент
}Ofl;
char *mesMain[] = //сообщения меню
{
"Включить новый элемент",
"Удалить элемент",
"Вывести таблицу",
"Выход"
};
//количество пунктов меню
int MesMainCount = sizeof( mesMain ) / sizeof( mesMain[0] );
//функции отработки пунктов меню
void ( *funcMain[] )()={AddElement,
DelElement,
OutTable,
Exit};
Ofl *table[SIZE]; //таблица-вектор
//Поиск элемента и места, куда вставить
//Вызывается при добавлении нового элемента
//параметры: ключ и адрес указателя в цепочке next, куда додавим новый элемент
//или NULL, если цепочка пуста
//возвращает true, если элемент найден и false, если не найден
bool Find(int num, Ofl **ppOfl)
{
Ofl *pOfl;
int i = num%SIZE; //функция расстановки - остаток от деления на SIZE=10
//результат - производный ключ
//найдем, есть ли уже заданный ключ
//*ppOfl в итоге будет указывать на предыдущий последний элемент,
//после которого мы вставим новый
for (*ppOfl=NULL,pOfl=table[i]; pOfl!=NULL; pOfl=pOfl->next)
{
*ppOfl = pOfl; //возвращаем указатель предыдущего последнего
//ищем искомый ключ
if (pOfl->key == num) //ключ равен!
return true; //выходим
}
return false; //не нашли, в *ppOfl будет указатель последнего (или NULL)
}
//добавление элемента в таблицу
void AddElement()
{
int num; //индекс нового элемента
char str[80];
Ofl *pOfl; //указатель на текущий элемент
Ofl *pOflPrev; //указатель на элемент, после которого вставим
cout << "Введите ключ элемента: ";
num = GetNum(); //вводим ключ
if(Find(num, &pOflPrev))
{
cout << "Элемент с таким ключем уже есть" << endl;
return;
}
pOfl = new Ofl; //создаем новый элемент
//заполняем поля
pOfl->key = num; //ключ
pOfl->next = NULL; //признак последнего элемента в цепочке
cout << "Введите строку: ";
cin.ignore();
cin.getline(str, 80);
pOfl->info = new char[strlen(str)+1];
strcpy(pOfl->info, str); //добавляем строку
//определимся со ссылкой на наш элемент
if (pOflPrev != NULL) //если вставляется не в начало цепочки
pOflPrev->next = pOfl; //то предыдущий будет на него ссылаться
else
table[num%SIZE] = pOfl; //если первый элемент, то запоминаем ссылку в массиве table
cout << "Элемент добавлен" << endl;
}
//удаление элемента с заданным ключем
void DelElement()
{
int i, num;
Ofl *pOflPrev, *pOfl, *pOflNext;
cout << "Введите ключ элемента: ";
num = GetNum(); //вводим ключ
i = num%SIZE; //функция расстановки - остаток от деления на SIZE=10
//результат - производный ключ
//по всем элементам цепочки для производного ключа
for (pOflPrev=NULL,pOfl=table[i]; pOfl; pOfl=pOflNext)
{
pOflNext = pOfl->next; //указатель на следующего
//сравниваем ключ
if (pOfl->key == num)
{ //выкинем элемент из цепочки указателей
if (pOflPrev == NULL) //первый элемент в цепочке?
table[i] = pOflNext; //пишем в массив table адрес следующего
else //иначе, предыдущий ссылается
pOflPrev->next = pOflNext; //на следующего
delete [] pOfl->info; //удаляем строку
delete pOfl; //и сам элемент
cout << "Элемент удален" << endl;
return;
}
pOflPrev=pOfl; //сдвигаем указатель на предыдущий элемент
}
//прошли всю цепочку, значит не нашли
cout << "Элемент не найден" << endl;
}
//вывод всей таблицы
void OutTable()
{
int i, count = 0; //посчитаем число элементов
Ofl *pOfl;
for(i=0; i<SIZE; i++) //по всем элементам таблицы
{
//по всем элементам цепочек
for (pOfl = table[i]; pOfl; pOfl=pOfl->next)
{
cout << "key = " << pOfl->key
<< ", info = " << pOfl->info
<< endl;
count++;
}
}
if (0 == count) //сообщение для пустой таблицы
cout << "Таблица пуста" << endl;
}
//выход, удалим все элементы
void Exit()
{
Ofl *pOfl, *pOflNext;
for (int i=0; i<SIZE; i++)
{
for (pOfl=table[i]; pOfl; pOfl=pOflNext)
{
pOflNext = pOfl->next; //запомним уазатель на следующего
delete [] pOfl->info; //удаляем текущего
delete pOfl;
}
}
}
//ввод числа
int GetNum()
{
int a;
cin >> a;
while ( !( cin.good() || cin.eof() ) || ( a < 0 ) )
{
cout << "Введите число! " << endl;
cin.clear();
cin.ignore();
cin >> a;
}
return a;
}
//показ меню с вводом номера строки
int ViewMenu(char** mes, int max)
{
int ret;
do
{
//меню
for ( int i = 0; i < max; i++ )
cout << i+1 << ". " << mes[i] << endl;
cout << "Ваш выбор: ";
//вводим число
ret = GetNum();
}
//проверим на допустимость
while ( ret < 1 || ret > max );
//вернем номер строки
return ret;
}
int main()
{
int ret;
system("chcp 1251 >> nul"); //чтобы писалось по-русски
// или так:
// locale::global(locale("Russian_Russia.866"));
do
{
ret = ViewMenu(mesMain, MesMainCount); //выводим меню, вводим номер строки
funcMain[ret-1](); //отрабатываем пункт меню
cout << "--------------------------------" << endl;
if (ret == MesMainCount) //последняя - выход
break;
}
while ( ret );
return 0;
}
б)
Код :
#include <locale>
#include <iostream>
using namespace std;
void ReadTableFromFile(void);
void WriteTableToFile(void);
void AddElement(void);
void DelElement(void);
void OutTable(void);
void Exit(void);
int GetNum(void);
const int SIZE = 10;
typedef struct Ofl
{
int key; //ключ элемента
int seek; //смещение поля info относительно начала строк в файле
int len; //длина info
Ofl *next; //указатель на след. элемент
}Ofl;
char *mesMain[] = //сообщения меню
{
"Включить новый элемент",
"Удалить элемент",
"Вывести таблицу",
"Выход"
};
//количество пунктов меню
int MesMainCount = sizeof( mesMain ) / sizeof( mesMain[0] );
//функции отработки пунктов меню
void ( *funcMain[] )()={AddElement,
DelElement,
OutTable,
Exit};
int StringsOff; //начало строк в файле
Ofl *table[SIZE]; //таблица-вектор
FILE *file = NULL; //указатель на открытый файл
char fName[256];
//чтение таблицы из файла
//формат файла:
//int StringsOff - начало строк в файле
//Ofl *table[SIZE] - таблица (вся), после каждого элемента цепочка элементов
//... - строки info
void ReadTableFromFile()
{
int i;
Ofl *pOfl, *pOflPrev;
cout << "Введите имя файла: ";
cin.getline(fName, 256); //вводим имя файла
file = fopen(fName, "r+b"); //открываем на чтение/запись, тип двоичный
if (NULL == file) //если нет такого
{
file = fopen(fName, "w+b"); //то создаем новый файл
if (file) //создался?
{ //запишем файл с пустой таблицей
//смещение строк равно сумме длин всех указателей
//и самого поля смещения
StringsOff = sizeof(Ofl*) * SIZE + sizeof(int);
//пишем смещение блока строк в файле
fwrite(&StringsOff, sizeof(int), 1, file);
//нулевой массив указателей
fwrite(table, sizeof(Ofl*), SIZE, file);
}
else
cout << "Ошибка создания файла" << endl;
}
else //файл есть
{ //читаем смещение блока строк в файле
fread(&StringsOff, sizeof(int), 1, file);
for (i=0; i<SIZE; i++) //читаем элементы таблицы
{ //читаем указатель в таблице
fread(&pOfl, sizeof(Ofl*), 1, file);
if (pOfl) //ненулевой - дальше будут элементы!
{
while(pOfl) //читаем цепочку элементов, пока указатель ненулевой
{
pOfl = new(Ofl); //создаем элемент
if (table[i] == NULL) //первый?
table[i] = pOfl; //записываем указатель в таблицу
else //непервый - делаем ссылку у предыдущего на текущего
pOflPrev->next = pOfl;
//читаем с файла элемент
fread(pOfl, sizeof(Ofl), 1, file);
pOflPrev = pOfl; //запоминаем текущего, как нового предыдущего
pOfl=pOfl->next; //у последнего в цепочке здесь будет нуль
}
}
else //нулевой - элементов нет!
table[i] = NULL;
}
}
fclose(file); //файл закрываем
}
//запись таблицы в файл (в конце работы)
void WriteTableToFile()
{
Ofl *pOfl;
int i, len;
fpos_t pos; //позиция в файле
void *pStrings; //адрес буфера для чтения массива строк
if (fName[0]) //проверим на наличие имени файла
{
file = fopen(fName, "r+b"); //открываем на чтение/запись
if (file) //открылся?
{
//прочитаем массив строк из файла
//определим длину файла
fseek(file, 0, SEEK_END); //в конец файла
fgetpos(file, &pos); //определяем позицию
fseek(file, StringsOff, SEEK_SET); //в начало массива строк!
len = (int)pos - StringsOff; //длина массива строк
pStrings = malloc(len); //выделим память под массив строк
fread(pStrings, len, 1, file); //прочитаем
fseek(file, 0, SEEK_SET); //позицию в начало файла!
//формируем файл!
fwrite(&StringsOff, sizeof(int), 1, file); //начало массива строк
//выводим таблицу
for (i=0; i<SIZE; i++)
{
//указатель начала цепочки (или 0)
fwrite(&table[i], sizeof(Ofl*), 1, file);
//выводим структуры цепочек
for (pOfl=table[i]; pOfl; pOfl=pOfl->next)
fwrite(pOfl, sizeof(Ofl), 1, file);
}
//запишем массив строк и подправим новое смещение массива строк!
StringsOff = ftell(file); //равно позиции после записи таблицы
fwrite(pStrings, len, 1, file); //пишем сохраненный массив строк
fseek(file, 0, SEEK_SET); //в начало файла!
fwrite(&StringsOff, sizeof(int), 1, file); //и пишем новое смещение массива строк
fclose(file); //файл закрываем
free(pStrings); //освобождаем память под строки
}
else
cout << "Ошибка открытия файла" << endl;
}
else
cout << "Имя файла не задано" << endl;
}
//Поиск элемента и места, куда вставить
//Вызывается при добавлении нового элемента
//параметры: ключ и адрес указателя в цепочке next, куда додавим новый элемент
//или NULL, если цепочка пуста
//возвращает true, если элемент найден и false, если не найден
bool Find(int num, Ofl **ppOfl)
{
Ofl *pOfl;
int i = num%SIZE; //функция расстановки - остаток от деления на SIZE=10
//результат - производный ключ
//найдем, есть ли уже заданный ключ
//*ppOfl в итоге будет указывать на предыдущий последний элемент,
//после которого мы вставим новый
for (*ppOfl=NULL,pOfl=table[i]; pOfl!=NULL; pOfl=pOfl->next)
{
*ppOfl = pOfl; //возвращаем указатель предыдущего последнего
//ищем искомый ключ
if (pOfl->key == num) //ключ равен!
return true; //выходим
}
return false; //не нашли, в *ppOfl будет указатель последнего (или NULL)
}
//добавление элемента в таблицу
void AddElement()
{
int num; //индекс нового элемента
char str[80];
Ofl *pOfl; //указатель на текущий элемент
Ofl *pOflPrev; //указатель на элемент, после которого вставим
cout << "Введите ключ элемента: ";
num = GetNum(); //вводим ключ
if(Find(num, &pOflPrev))
{
cout << "Элемент с таким ключем уже есть" << endl;
return;
}
pOfl = new Ofl; //создаем новый элемент
//заполняем поля
pOfl->key = num; //ключ
pOfl->next = NULL; //признак последнего элемента в цепочке
cout << "Введите строку: ";
cin.ignore();
cin.getline(str, 80);
if (fName[0]) //проверим на наличие имени файла
{
file = fopen(fName, "r+b"); //открываем
if (file) //открылся?
{
//определим смещение строки в массиве строк
fseek(file, 0, SEEK_END); //становимся в конец файла
pOfl->seek = ftell(file)-StringsOff; //запоминаем позицию, как
// смещение в массиве строк поля info
pOfl->len = strlen(str)+1; //запоминаем длину строки
fwrite(str, pOfl->len, 1, file); //и пишем строку в конец файла
fclose(file); //закрываем файл
//определимся со ссылкой на наш элемент
if (pOflPrev != NULL) //если вставляется не в начало цепочки
pOflPrev->next = pOfl; //то предыдущий будет на него ссылаться
else
table[num%SIZE] = pOfl; //если первый элемент, то запоминаем ссылку в массиве table
cout << "Элемент добавлен" << endl;
}
else
cout << "Ошибка открытия файла" << endl;
}
else
cout << "Имя файла не задано" << endl;
}
//удаление элемента с заданным ключем
//файл не используется.
//удаление происходит только в памяти!
void DelElement()
{
int i, num;
Ofl *pOflPrev, *pOfl, *pOflNext;
cout << "Введите ключ элемента: ";
num = GetNum(); //вводим ключ
i = num%SIZE; //функция расстановки - остаток от деления на SIZE=10
//результат - производный ключ
//по всем элементам цепочки для производного ключа
for (pOflPrev=NULL,pOfl=table[i]; pOfl; pOfl=pOflNext)
{
pOflNext = pOfl->next; //указатель на следующего
//сравниваем ключ
if (pOfl->key == num)
{ //выкинем элемент из цепочки указателей
if (pOflPrev == NULL) //первый элемент в цепочке?
table[i] = pOflNext; //пишем в массив table адрес следующего
else //иначе, предыдущий ссылается
pOflPrev->next = pOflNext; //на следующего
delete pOfl; //удаляем элемент
cout << "Элемент удален" << endl;
return;
}
pOflPrev=pOfl; //сдвигаем указатель на предыдущий элемент
}
//прошли всю цепочку, значит не нашли
cout << "Элемент не найден" << endl;
}
//вывод всей таблицы
void OutTable()
{
int i, count = 0; //посчитаем число элементов
Ofl *pOfl;
char str[80];
if (fName[0]) //имя файла задано?
{
file = fopen(fName, "r+b"); //открываем файл
if (file) //открылся?
{
for(i=0; i<SIZE; i++) //по всем элементам таблицы
{
//по всем элементам цепочек
for (pOfl = table[i]; pOfl; pOfl=pOfl->next)
{
fseek(file, pOfl->seek+StringsOff, SEEK_SET); //становимся в позицию, где начинается строка
fread(str, pOfl->len, 1, file); //читаем нужное число байт
cout << "key = " << pOfl->key
<< ", info = " << str
<< endl;
count++;
}
}
fclose(file); //файл закрываем
}
else
cout << "Ошибка открытия файла" << endl;
}
else
cout << "Имя файла не задано" << endl;
if (0 == count) //сообщение для пустой таблицы
cout << "Таблица пуста" << endl;
}
//выход, удалим все элементы
void Exit()
{
Ofl *pOfl, *pOflNext;
WriteTableToFile(); //сохраним таблицу в файле
//удалим элементы таблицы со всеми цепочками
for (int i=0; i<SIZE; i++)
{
for (pOfl=table[i]; pOfl; pOfl=pOflNext)
{
pOflNext = pOfl->next; //запомним уазатель на следующего
delete pOfl;
}
}
}
//ввод числа
int GetNum()
{
int a;
cin >> a;
while ( !( cin.good() || cin.eof() ) || ( a < 0 ) )
{
cout << "Введите число! " << endl;
cin.clear();
cin.ignore();
cin >> a;
}
return a;
}
//показ меню с вводом номера строки
int ViewMenu(char** mes, int max)
{
int ret;
do
{
//меню
for ( int i = 0; i < max; i++ )
cout << i+1 << ". " << mes[i] << endl;
cout << "Ваш выбор: ";
//вводим число
ret = GetNum();
}
//проверим на допустимость
while ( ret < 1 || ret > max );
//вернем номер строки
return ret;
}
int main()
{
int ret;
system("chcp 1251 >> nul"); //чтобы писалось по-русски
// или так:
// locale::global(locale("Russian_Russia.866"));
ReadTableFromFile(); //читаем таблицу из файла (или создаем новый)
do
{
ret = ViewMenu(mesMain, MesMainCount); //выводим меню, вводим номер строки
funcMain[ret-1](); //отрабатываем пункт меню
cout << "--------------------------------" << endl;
if (ret == MesMainCount) //последняя - выход
break;
}
while ( ret );
return 0;
}
Консультировал: Лысков Игорь Витальевич (Старший модератор)
Дата отправки: 01.06.2012, 03:39
Команда портала RFPRO.RU благодарит Вас за то, что Вы пользуетесь нашими услугами. Вы только что прочли очередной выпуск рассылки. Мы старались.
Пожалуйста, оцените его. Если совет помог Вам, если Вам понравился ответ, Вы можете поблагодарить автора -
для этого в каждом ответе есть специальные ссылки. Вы можете оставить отзыв о работе портале. Нам очень важно знать Ваше мнение.
Вы можете поближе познакомиться с жизнью портала, посетив наш форум, почитав журнал,
который издают наши эксперты. Если у Вас есть желание помочь людям, поделиться своими знаниями, Вы можете зарегистрироваться экспертом.
Заходите - у нас интересно!