
- •Структуры и алгоритмы обработки данных: план курса
- •Лабораторные работы
- •Литература
- •Ста: Лекция №1 - Введение. Данные в памяти программ
- •Введение
- •Модель памяти в прикладных программах
- •Int main ()
- •Int main ()
- •Int main ()
- •Int main ()
- •Char buf[ 2000000 ];
- •Return f(); // бесконечно долго вызываем сами себя // вызываем переполнение стека через время
- •Int main ()
- •Return f();
- •Int main ()
- •Delete[] p; // Освобождаем память
- •Int main ()
- •Int a[ n ]; // ошибка, размер нельзя вычислить во время компиляции
- •Delete[] p;
- •Int main ()
- •Проблема фиксированного размера массивов
- •Int main ()
- •Int main ()
- •Int main ()
- •Динамически растущие массивы (векторы)
- •Int main ()
- •Struct IntegerVector
- •Int * m_pData;
- •Int m_nUsed;
- •Int m_nAllocated;
- •Void IntegerVectorDestroy ( IntegerVector & _vector )
- •Int main ()
- •#Ifndef _integer_vector_hpp_
- •#Include "integer_vector.Hpp"
- •Void IntegerVectorRead ( IntegerVector & _vector, std::istream & _stream );
- •Ста: Лекция №2 - Связные списки.
- •Всегда ли хорош вектор?
- •Связные списки
- •Ста: Лекция №3 - Реализация и использование простейших атд
- •Абстрактные типы данных (атд)
- •Атд “Список” ( “Последовательность” )
- •Атд “Стек”
- •Атд “Очередь”
- •Ста: Лекция №7 - Деревья
- •Основные сведения о деревьях
- •Обход деревьев
- •Атд “Дерево”
- •Типичные структуры данных для n-арных деревьев
- •1. Массив меток и массив родительских индексов.
- •2. Массив меток и заголовок со списками дочерних узлов.
- •3. Динамическая структура с указателями
- •В результате ее выполнения в динамической памяти формируется структура объектов, в существенной степени напоминающая оригинальный пример из описания понятия деревьев:
- •Бинарные деревья
- •Глава 3 “Элементарные структуры данных”
- •Глава 4 “Абстрактные типы данных”
- •Глава 10 “Элементарные структуры данных” (подразделы 10.1-10.3)
- •Глава 2 “Основные абстрактные типы данных” (подразделы 2.1-2.4)
- •Глава 6 “Элементарные методы сортировки”.
- •Глава 5 “Рекурсия и деревья”.
Void IntegerVectorRead ( IntegerVector & _vector, std::istream & _stream );
а в файл реализации - тело новой функции:
void IntegerVectorRead ( IntegerVector & _vector, std::istream & _stream )
{
while ( true )
{
int temp;
_stream >> temp;
if ( _stream )
IntegerVectorPushBack( _vector, temp );
else
break;
}
}
Затем в обеих программах нужно заменить такие блоки на вызовы функции:
1)
#include <iostream>
#include <cstring>
int main ()
{
// Приглашение пользователя ко вводу
std::cout << "Input integers, stop with Ctrl+Z: ";
// Создаем вектор и инициализируем его одним вызовом
IntegerVector v;
IntegerVectorInit( v );
// Считываем данные с консоли
IntegerVectorRead( v, std::cin );
// Количество введенных данных сохранено в переменной m_nUsed в векторе
// Выводим данные в обратном порядке
for ( int i = v.m_nUsed - 1; i >= 0; i-- )
std::cout << v.m_pData[ i ] << ' ';
std::cout << std::endl;
// Просим вектор освободить выделенные им ресурсы
IntegerVectorDestroy( v );
}
2)
#include "integer_vector.hpp"
#include <iostream>
int main ()
{
// Приглашение пользователя ко вводу
std::cout << "Input integers, stop with Ctrl+Z: ";
// Создаем и инициализируем вектор для входных данных
IntegerVector inputData;
IntegerVectorInit( inputData );
// Считываем данные с консоли
IntegerVectorRead( inputData, std::cin );
// Создаем и инициализируем вектор для выходных данных
IntegerVector outputData;
IntegerVectorInit( outputData );
// Формируем последовательность частичных сумм
int currentSum = 0;
for ( int i = 0; i < inputData.m_nUsed; i++ )
{
currentSum += inputData.m_pData[ i ];
IntegerVectorPushBack( outputData, currentSum );
}
// Выводим результаты
for ( int i = 0; i < outputData.m_nUsed; i++ )
std::cout << outputData.m_pData[ i ] << ' ';
std::cout << std::endl;
// Освобождаем выделенные векторами ресурсы
IntegerVectorDestroy( inputData );
IntegerVectorDestroy( outputData );
}
Отметим, что явная передача входного потока std::cin в качестве аргумента функции чтения последовательности в вектор добавляет гибкость к реализованной функциональности. Если потребуется считать последовательность не с консоли, а с дискового файла, тело функции не придется изменять вообще. Ниже приведен пример чтения последовательности из файла:
#include <fstream>
...
// Создаем и инициализируем вектор для входных данных
IntegerVector inputData;
IntegerVectorInit( inputData );
// Открываем входной файловый поток
std::ifstream f( “input.txt”, std::ios_base::in );
// Считываем данные из открытого файла
IntegerVectorRead( v, f );
При написании программ всегда следует задумываться о возможности повторного использования частей, о их универсальности. Такой подход позволяет создавать легко расширяемые, функционально богатые и красивые с профессиональной точки зрения программы.
Выводы
Таким образом, в данной лекции было сформулировано ключевое требование к программам - корректно и эффективно адаптироваться к подаваемому во время выполнения объему входных данных. Была предложена простейшая структура данных для хранения последовательностей произвольной длины - динамически растущий массив (вектор). На примере двух задач была продемонстрирована реализация такой структуры, а также показаны элементарные приемы программирования, улучшающие структуру кода, его читабельность и гибкость к изменениям.