- •Экзамен 374 Предварительные рассуждения Вступительное слово
- •Исторические факты
- •Начнем!
- •Проба пера
- •Открытие сохраненного проекта
- •Вывод данных
- •Типы данных
- •Хороший стиль программирования
- •Переменные и константы
- •Практический пример
- •Ввод данных
- •Например:
- •Пример:
- •Арифметические операции с числами
- •Литералы
- •Некоторые примеры
- •Домашнее задание
- •Напишите программу, которая вводит число из трех цифр, разделяет число на отдельные цифры и печатает их отдельно друг от друга с тремя пробелами между ними. Преобразование типов
- •Перечисляемые типы
- •Типичная ошибка
- •Хороший стиль программирования
- •Типичная ошибка
- •Выражения
- •Оператор if
- •Структура программы
- •Логические операции
- •Структура множественного выбора switch
- •Практический пример
- •Цикл for
- •Практический пример
- •Цикл do-while
- •Домашнее задание
- •Вызов функции
- •Прототипы функций
- •Разбор программы
- •Область видимости
- •Аргументы по умолчанию
- •Встраивание
- •Перегрузка функций
- •Учебный пример перегруженных функций. Иллюстрация перегрузки
- •Результат работы программы
- •Практические примеры
- •Домашнее задание
- •Примеры домашней работы урока 1 Пример №1
- •Как работает программа
- •Пример №2
- •Как работает программа
- •Примеры домашних работ на создание функций Пример №1
- •Как работает программа
- •Пример №2
- •Как работает программа
- •Массивы
- •Объявление массивов
- •Примеры использования массивов
- •Программа 1
- •Программа 2
- •Обратите внимание!
- •Типичная ошибка программирования
- •Типичная ошибка программирования
- •Программа 3
- •Типичная ошибка программирования
- •Замечание по технике программирования
- •Программа 4
- •Программа 5
- •Программа нахождения минимального и максимального элементов массива
- •Сортировка массивов
- •Домашнее задание
- •Что такое указатели?
- •За кулисами...
- •Как работать с указателями?..
- •Зачем нужны указатели?
- •Указатели и Массивы.
- •Примеры задач
- •Пример 1
- •Пример 2
- •Пример 3
- •Указатели - аргументы функций.
- •Ссылочные параметры
- •Примеры решения задач
- •Домашнее задание
- •Операторы свободной памяти new и delete
- •Функции работы со строками из библиотеки обработки строк
- •Пример 1.
- •Пример2
- •Пример 3
- •Пример задачи на новый материал
- •Домашнее задание
- •Двухмерные массивы, как частный случай многомерных массивов
- •Программа.
- •Результаты работы программы.
- •Многомерные динамические массивы
- •Пример на многомерные динамические массивы
- •Домашнее задание
- •Рекурсия
- •Рекурсии или итерации
- •Указатели на функции
- •Пример №1
- •Результат выполнения программы:
- •Пример №2
- •Результат выполнения программы
- •Пример №3
- •Результаты выполнения программы
- •Определения структур
- •Пример #1 на использование структур
- •Пример #2 на использование структур
- •Оператор указателя на структуру
- •Домашнее задание
- •Тест по c Группа ___________________ф. И. О. ______________________
- •Объектно-ориентированное программирование.
- •Наследование (Inheritance).
- •Инкапсуляция (Encapsulation).
- •Определение класса
- •Конструкторы и деструкторы Инициализация объектов класса: конструкторы
- •Основное назначение конструкторов - инициализация объектов.
- •Использование конструкторов с аргументами по умолчанию
- •Если параметры не передаются конструктору, в определении объекта не нужно включать пустые круглые скобки.
- •Использование деструкторов
- •Когда вызываются конструкторы и деструкторы.
- •Домашнее задание
- •Конструктор копирования
- •Синтаксис конструктора копирования
- •Памятка
- •Пример использования конструктора копирования.
- •Перегруженные конструкторы
- •Экскурс в историю
- •Послесловие к примеру
- •Маленькое замечание
- •Домашнее задание
- •Создание класса ''строка''
- •Перегрузка операций.
- •Общие принципы перегрузки операторов.
- •Преобразования, определяемые классом
- •Пример строкового класса с перегруженными операторами
- •Домашнее задание
- •Дружественные функции (Friend Functions)
- •Пример строкового класса с перегруженными операторами и дружественными функциями
- •Перегрузка операторов new и delete
- •Перегрузка оператора индексирования
- •Класс вектор. Часть1.
- •Класс вектор. Часть 2.
- •Класс вектор. Часть 3.
- •Домашнее задание
- •Наследование (Inheritance). Часть 1.
- •Наследование (Inheritance). Часть 2.
- •Множественное наследование (multiple inheritance)
- •Пример множественного наследования
- •Домашнее задание
- •Статические члены данных
- •Раннее и позднее связывание
- •Виртуальные функции
- •Пример.
- •Абстрактные классы
- •Виртуальный базовый класс
- •Практический пример
- •Домашнее задание
- •Потоки ввода-вывода.
- •Iostream.H: stream - поток, "I" - сокр. Input - ввод, "o" - сокр. Output - вывод.
- •Предопределенные потоки.
- •Операции помещения в поток и извлечения из потока.
- •Файловый ввод-вывод с применением потоков.
- •Конструкторы файловых потоков.
- •Функции для открытия и закрытия файлов.
- •Функции для обмена с потоками.
- •Часто применяемые функции потока.
- •Ввод/вывод массива в/из файл(-а).
- •Практический пример: перекодировка файла.
- •Домашнее задание
- •Немного о файлах...
- •И снова файлы...
- •Пример "Телефонная книга"
- •Файл abonent.H
- •Форматирование данных при обменах с потоками.
- •Состояние потока.
- •Использование аргументов командной строки.
- •Ввод/вывод в с.
- •Домашнее задание
- •Определение шаблонов функций
- •Переопределение шаблонов функций
- •Шаблоны классов
- •Шаблонный класс вектор
- •Шаблонный класс вектор
- •Шаблонный класс вектор
- •Введение
- •Обработка исключительных ситуаций
- •Практический пример
- •Программа
- •Домашнее задание
- •Экзамен
Программа 5
// бросание шестигранной кости 6000 раз
#include < iostream.h >
#include < stdlib.h >
#include < time.h >
void main()
{
const int arraySize = 6;
int face, // объявление переменной face
frequency[arraySize] = {0}; /* объявление массива и инициализация
его нулевыми значениями */
srand (time(NULL));
for(int i = 0; i < 6000; i++)
{
// имитация броска игральной кости
face = rand() % 6 + 1;
/* увеличение значения соответсвующего элемента
массива на 1 */
++ frequency[face];
}
// вывод результатов на экран
cout << "Face" << '\t' << "Frequency\n";
for (face = 0; face < arraySize; face++)
cout << face + 1 << '\t' << frequency[face] << '\n';
}
Программа нахождения минимального и максимального элементов массива
Рассмотрим задачу нахождения минимального и максимального элементов массива.
#include <iostream.h>
void main()
{
const int n = 10; // Размерность массива
float a[n]; // Объявление массива
for(int i = 0; i < n; i++)
{
cout << "Input the a[" << i << "] element:\t";
cin >> a[i]; // Ввод значений массива
}
float Min, Max; // Минимум и максимум
Min = Max = a[0]; // инициализируются первым элементом массива
for(i = 1; i < n; i++) // Сравнение с текущим элементом
if(a[i] > Max)
Max = a[i]; // Текущий максимальный элемент
else if(a[i] < Min)
Min = a[i]; // Текущий минимальный элемент
cout << endl;
for(i = 0; i < n; i++)
cout << a[i] << "\t"; // Вывод исходного массива
cout << "\nMin element:\t" << Min << "\nMax element:\t" << Max << endl;
// Вывод результатов поиска
}
В данной программе обе переменные Min и Max изначально инициализируются первым элементом массива. Затем в цикле идет проверка: если текущий элемент массива больше Max, то переменной Max присваивается текущий элемент. И наоборот, если текущий элемент массива меньше Min, то переменной Min присваивается текущий элемент.
Сортировка массивов
Исторически зарождение методов машинной сортировки можно отнести еще к прошлому столетию, и за столь длительное время многие специалисты успели испробовать свои силы в этой области. Нет нужды говорить о важности самой области. Практически сортировка и поиск в той или иной мере присутствуют во всех приложениях; в частности, при обработке больших объемов данных эффективность именно этих операций определяет эффективность, а иногда и работоспособность всей системы. Поэтому можно сказать, что достаточно четкие представления об этой области нужны при решении любой задачи на ЭВМ как обязательные элементы искусства програмирования. В этом уроке мы обсудим простейшие известные способы сортировки и поиска.
Программа сортирует значения массива, используя один из самых простых способов. Сначала она сравнивает элементы массива a[0] и а[1], меняя местами их значения, если они не упорядочены, затем проделывает то же самое с элементами а[1] и а[2], а[2] и а[3] и т.д.. При выполнении этой последовательности операций элементы с большим значанием будут продвигаться вправо, и на самом деле элемент с наибольшим значением займет положение а[7]. При многократном выполнении этого процесса соответствующие элементы попадут в позиции а[6], а[5] и т.д., так что в конце концов все элементы будут упорядочены.
На рисунке представлено действие данной сортировки на восьми числах.
Рисунок - Пузырьковая сортировка
|
Ряд чисел удобно представить не горизонтально, а вертикально, чтобы элемент а[7] был сверху, а а[0] - снизу. Используемая при сортировке техника, получила название метода пузурька (пузырьковая сортировка ), потому что большие элементы, подобно пузурькам, "всплывают" на соответствующую позицию.
Сортировка выполняется с помощью вложенного цикла for. Если необходима перестановка, она выполняется тремя присваиваниями
hold = a[i];
a[i] = a[i+1];
a[i+1] = hold;
где дополнительная переменная hold временно хранит одно из двух переставляемых значений. Перестановку нельзя выполнить двумя присваиваниями
a[i] = a[i+1];
a[i+1] = a[i];
т.к. после первого присваивания значение а[i] будет потеряно, следовательно необходима дополнительная переменная.
/* Программа сортирует значения массива в возрастающем
порядке методом пузурька*/
#include < iostream.h >
void main()
{
const int arraySize = 8;
//объявление и инициализация массива
int a[arraySize] = {6, 10, 5, 0, 2, 11, 7, 3};
int hold;
//Следующая строка выводит на экран сообщение:
//Элементы данных в исходном порядке:
cout <<"\nThe enteried data is : \n";
// вывод массива на экран
for (int i = 0; i < arraySize; i++)
cout << a[i] << '\t';
// сортировка массива
bool flag = true; //флаг сотировки
// (false-массив не отсортирован, true-массив отсортирован)
// Изначально предполагаем оптимистический вариант
// объявили вспомогательные переменные
for (int j = 1;; j++) //создаем бесконечный цикл
{
for(i = 0; i <arraySize-j; i++)
if (a[i] > a[i+1])
{
//выполняем перестановку
hold = a[i];
a[i] = a[i+1];
a[i+1] = hold;
flag = false;
}
if(flag) //массив отсортирован?
break;//да - выход из цикла
flag = true; //иначе - устанавливаем флаг сортировки
}
//Следующая строка выводит на экран сообщение:
//Элементы данных в порядке возрастания:
cout << "\nIncrements data's series:\n";
// вывод массива на экран
for (i = 0; i < arraySize; i++)
cout << a[i] << '\t';
cout << '\n';
}
Главное достоинство пузырьковой сортировки заключается в простоте ее программирования. Однако, выполняется она медленно. Это становится очевидным при сортировке больших массивов.
Поиск
Часто, программисту приходится работать с большими объемами данных, хранящимися в виде массивов. Может оказаться необходимым определить, содержит ли массив, значение которое соответствует определенному ключевому значению. Процесс нахождения какого-то элемента массива называет поиском. В данном уроке мы рассмотрим наиболее простой способ поиска - линейный поиск.
Линейный поиск сравнивает каждый элемент массива с ключом поиска. Поскольку массив не упорядочен, вполне вероятно, что отыскиваемое значение окажется первым элементом массива. Но в среднем, однако, программа должна сравнить с ключом поиска половину элементов массива.
#include <iostream.h >
int linearSearch(int [], int, int);
void main()
{
const int arraySize = 100;
int a[arraySize], searchKey, element;
for( int x = 0; x < arraySize; x++)
a[x] = 2*x;
//Следующая строка выводит на экран сообщение
//Введите ключ поиска:
cout << "Please, enter the key: ";
cin >> searchKey;
element = linearSearch(a, searchKey, arraySize);
if(element != -1)
//Следующая строка выводит на экран сообщение
//Найдено значение в элементе
cout << "\nThe key was found in element " << element << '\n';
//Следующая строка выводит на экран сообщение
//Значение не найдено
else cout << "\nValue not found ";
}
int linearSearch (int array[], int key, int sz)
{
for (int i = 0; i < sz; i++)
if(array[i] == key)
return i;
return -1;
}
Метод линейного поиска хорошо работает для небольших или для несортированных массивов. Однако, для больших массивов линейный поиска неэффективен. Если массив отсортирован, можно использовать высокоэффективный метод двоичного поиска.
Алгоритм двоичного поиска исключает половину еще непроверенных элементов массива после каждого сравнения. Алгоритм определяет местоположение среднего элемента массива и сравнивает его с ключом поиска. Если они равны, то ключ поиска найден и выдается индекс этого элемента. В противном случае задача сокращается на половину элементов массива. Если ключ поиска меньше, чем средний элемент массива, то дальнейший поиск осуществляется в первой половине массива, а если больше, то во второй половине. Если ключ поиска не совпадает со средним элементом выбранного подмассива (части исходного массива), то алгоритм повторно применяется и сокращает область поиска до четверти исходного массива. Поиск продолжается до тех пор, пока ключ поиска не станет равным среднему элементу или пока оставшийся подмассив содержит хотя бы один элемент, не равный ключу поиска.
В наихудшем случае двоичный поиск в массиве из 1024 элементов потребует только 10 сравнений. Повторяющееся деление 1024 на 2 (поскольку после каждого сравнения мы можем исключить половину элементов массива) дает 512, 256, 128, 64, 32, 16, 8, 4, 2 и 1. Число 1024 (210) делится на 2 только десять раз. Деление на 2 эквивалентно одному сравнению в алгоритме двоичного поиска. Массив из 1048576 (220) элементов требует для нахождения ключа поиска самое большее 20 сравнений. Массив из одного миллиарда элементов требует для нахождения ключа поиска максимум 30 сравнений. Это огромное увеличение эффективности по сравнению с линейным поиском, который в среднем требует числа сравнений, равного половине числа элементов в массиве. Для миллиарда элементов выигрыш равен разнице между 500 миллионами сравнений и 30 сравнениями! Максимальное количество сравнений, необходимое для двоичного поиска в любом отсортированном массиве, может быть определено как первый показатель степени, при возведении в который числа 2 будет превышено число элементов в массиве.
Приведенная ниже Программа представляет итеративную версию функции binarySearch, реализующей алгоритм двоичного поиска. Функция получает четыре аргумента - массив целых чисел b, целое число searchKey, индекс массива low и индекс массива high. Если ключ поиска не соответствует среднему элементу массива, то устанавливается такое значение индекса low или high, что дальнейший поиск проводится в меньшем подмассиве. Если ключ поиска меньше среднего элемента, индекс high устанавливается как middle - 1, и поиск продолжается среди элементов от low до middle - 1. Если ключ поиска больше среднего элемента, индекс low устанавливается как middle + 1, и поиск продолжается среди элементов от middle - 1 до high. Программа использует массив из 15 элементов. Степень двойки для первого числа, большего, чем количество элементов в данном массиве, равна 4 (16 = 2 ), так что для нахождения ключа поиска нужно максимум четыре сравнения. Функция printRow выводит каждый подмассив в процессе двоичного поиска. Средний элемент в каждом подмассиве отмечается символом звездочки (*), чтобы указать тот элемент, с которым сравнивается ключ поиска.
Программа |
// двоичный поиск в массиве #include <iostream.h>
int binarySearch(int [], int, int, int, int); void printRow(int [], int, int, int, int);
void main( void ) { const int arraySize = 15; int a[arraySize], key, result;
// инициализация элементов массива for ( int i = 0; i < arraySize; i++) a[i] = 2*i;
cout << "Input key, a number in [0,28]: "; cin >> key;
// вызов функции binarySearch и присвоение, // возвращаемого ею значения переменной result result = binarySearch(a, key, 0, arraySize -1, arraySize); if(result != -1) cout << '\n' << key << " found in " << result << " element of array\n"; else cout << '\n' << key << " not found \n"; }
// Двоичный поиск int binarySearch(int b[], int searchKey, int low, int high, int size) { int middle; while (low <= high) { middle = (low + high) / 2;
printRow(b, low, middle, high, size);
if (searchKey == b[middle]) return middle; else if (searchKey < b[middle]) high = middle - 1; else low = middle + 1; } return -1; }
// печать одной строки, показывающей текущую // текущую обрабатываемую часть массива void printRow(int b[], int low, int mid, int high, int size) { for(int i = 0; i < size; i++) if (i < low || i > high) cout << " "; else { if (i*2 < 10) cout << " "; else cout << " ";
if(i == mid) cout << b[i] << '*'; /* отметить среднее значение */ else cout << b[i] << ' '; } cout << '\n'; } |
Фрагмент кода
if (i*2 < 10) cout << " ";
else cout << " ";
используется, для форматирования выходных данных. Иначе тот же результат можно получить, используя манипулятор потока setw(), как показано во фрагменте ниже. Обращение setw(3) определяет, что следующая выходная величина будет напечатана с шириной (размером) поля 3, т.е. ее значение будет содержать по крайней мере 3 символьных позиции. Если длина выходной величины менее 3 символов, она по умолчанию быдет выравнена в поле по правому символу. Если длина выходной величины более 3, размер поля будет увеличен, чтобы вместить полную величину. Программы, использующие подобное обращение, должны содержать директиву
#include<iomanip.h>
Другим манипулятором потока является обращение endl (аббревиатура словосочетания "end line"). Манипулятор endl выводит символ новой строки. Его использование не требует подключения файла iomanip.h
/*функция printRow с использованием манипуляторов потока*/
void printRow(int b[], int low, int mid, int high, int size)
{
for(int i = 0; i < size; i++)
if (i < low || i > high) cout << " ";
else if(i == mid)
cout << setw(3) << b[i] << '*';
else
cout << setw(3) << b[i] << ' ';
cout << endl;
}