Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Skripnik.doc
Скачиваний:
1
Добавлен:
28.10.2018
Размер:
209.92 Кб
Скачать

1. Внутренняя сортировка (сортировка массивов).

Сортировка – это упорядочение данных по некоторому признаку(ключу).

От внешней отличается тем, что необходим прямой доступ к элементам.

1.Прямое включение: Элементы мысленно делятся на уже упорядоченную последовательность a1,a2,…,ai-1 и неупорядоченную последовательность. При каждом шаге, начиная со второго, т.е. i=2 и увеличивая i каждый раз на единицу, из неупорядоченной последовательности извлекается i-й элемент и перекладывается в упорядоченную последовательность, при этом он вставляется на нужное место.

Алгоритм : Упорядочен элемент a1. В начале i-го шага упорядочена часть a1,a2,…,ai-1. В эту последовательность включаем на свое место элемент ai. Для этого сравниваем ai-1 c ai; если ai-1<= ai, то оставляем ai на месте, иначе меняем местами ai и ai-1. Выполняем такие сравнения до тех пор, пока не будет найден элемент ak<ai, либо не будет достигнута левая граница массива. Такой типичный случай повторяющегося процесса с двумя условиями окончания позволяет воспользоваться приемом “барьера” (sentinel). Здесь его легко применить, поставив барьер a0 со значением x. Это делается для того, чтобы не контролировать выход за левую границу массива; задаем фиктивно a0 на i-м месте: a0:=ai

2. Прямой выбор Этот прием основан на следующих двух принципах: выбирается элемент с наименьшим ключом; он меняется местами с первым элементом a1. Затем этот процесс повторяется с оставшимися n-1 элементами, n-2 элементами и т.д. до тех пор, пока не останется один самый большой элемент. Такой метод в некотором смысле противоположен прямому включению. При прямом включении на каждом шаге рассматриваются только один очередной элемент неупорядоченной последовательности и все элементы упорядоченной, среди которых отыскивается точка включения; при прямом выборе для поиска одного элемента с наименьшим ключом просматриваются все элементы исходной последовательности, и найденный помещается как очередной элемент в готовую последовательность.

3. Прямой обмен. Этот алгоритм основывается на сравнении и смене мест для пары соседних элементов и продолжении этого процесса до тех пор, пока не будут упорядочены все элементы. Мы повторяем проходы по массиву, сдвигая каждый раз наименьший элемент оставшейся последовательности к левому концу массива. Если рассматривать массивы как вертикальные, а не горизонтальные построения, то элементы можно интерпретировать как пузырьки в чане с водой, причем вес каждого соответствует его ключу. В этом случае при каждом проходе один пузырек как бы поднимается до уровня, соответствующего его весу. Такой метод известен под именем “пузырьковая сортировка”. На каждом шаге алгоритма массив просматривается от конца к началу. При этом просмотре осуществляется попарное сравнение соседних элементов и их перестановка, если надо. Затем изменяем индекс.

Этот алгоритма допускает улучшения. Очевидный прием улучшения этого алгоритма – запоминать, были или не были перестановки в процессе некоторого прохода. Если в последнем проходе перестановок не было, то алгоритм можно заканчивать. Это улучшение, однако, можно улучшить, если запоминать не только сам факт, что обмен имел место, но и положение (индекс) последнего обмена. Ясно, что все пары соседних элементов выше этого индекса k уже находятся в желаемом порядке. Поэтому просмотр можно заканчивать на этом индексе, а не идти до заранее определенного нижнего предела для i.

2. Типы данных языка программирования С++. Организация циклов

Данные - это объекты, над которыми возможно выполнение некоторых действий, в результате которых получаются некоторые значения. В качестве таких объектов выступают переменные и константы. Данные могут иметь три характеристики: имя, тип, значение.

Тип данных определяет:

Формат представления в памяти компьютера

Множество допустимых значений, которые может принимать принадлежащая к выбранному типу переменная или константа

Множество допустимых операций, применимых к этому типу.

Основные типы данных:

Простые типы Целочисленные типы

Логический тип Символьный тип

Перечисляемый тип Интервальный тип

Вещественный тип Структурированные типы

Массив Множество Запись

В языках C/C++ данные представляются одним из восьми базовых типов: char, int, float, double, void, bool, перечисления и указатели. Остановимся на каждом из типов данных.

Текст (char) представляет собой последовательность символов, таких как a, Z, ?,которые могут быть разделены пробелами. Обычно каждый символ занимает 8 бит, или один байт, с диапазоном значений от 0 до 255.

Целые числа (int) находятся в диапазоне от -32768 до 32767 и занимают 16 бит, т.е. два байта, или одно слово.

Числа с плавающей запятой одинарной точности (float) могут представляться как в фиксированном формате, например число л (3,14159), так и в экспоненциальном (7,56310). Диапазон значений — +/-3,4Е-38—3,4Е+38, размерность — 32 бита, т.е. 4 байта, или 2 слова.

Числа с плавающей запятой двойной точности (double) имеют диапа­зон значений от +/-1,7Е-308 до +/-1,7Е+308 и размерность 64 бита, т.е. 8 байтов, или 4 слова.

Перечисления.

Переменная, которая может принимать значение из некоторого списка целых значений, называется переменной перечислимого типа или перечислением.

Объявление перечисления начинается с ключевого слова enum и имеет следующую форму:

enum имя_тега_перечисления {список_перечисления}

описатель [,описатель...];

Тип данных void, как правило, применяется в функциях, не возвращающих никакого значения. Этот тип данных также можно использовать для создания обобщенных указателей.

Указатели, в отличие от переменных других типов, не содержат данных в обычном понимании этого слова. Вместо этого указатели содержат адреса памяти, где хранятся данные. Переменные типа данных bool в C++ могут содержать только одну из двух констант: true или false.

Организация циклов

1. Оператор цикла с постусловием. Обычно используется тогда, когда число повторений цикла заранее неизвестно, а определяется в процессе его выполнения. Оператор цикла с постусловием имеет вид:

do <тело цикла> while <условие>;

При выполнении этого оператора цикла операторы тела цикла выполняются хотя бы один раз. После выполнения операторов тела цикла проверяется <условие>. Если его значение TRUE , то операторы тела цикла повторяются, в противном случае цикл завершает свою работу.

2. Оператор цикла с предусловием. Используется тогда, когда число повторений неизвестно заранее, причем, при некоторых значениях исходных данных

3. Текстовые файлы в С++.

В программировании часто встречаются задачи, в которых обрабатываемые данные требуется сохранить для последующего использования. Для этого можно использовать файлы. Файл - это именованная область памяти на внешнем носителе. В файлах может храниться информация различной природы.

Пакет классов С++ работает с файловым вводом/выводом во многом так же, как он делает это со стандартным вводом/выводом. Чтобы записать данные в файл, необходимо создать потоковый объект и воспользоваться такими методами класса ostream, как операция вставки << или функция write(). Для чтения файла создается потоковый объект и применяются такие методы класса istream, как операция извлечения >> или функция get(). Однако файлы требуют более высокого уровня управления, чем стандартные потоки ввода/вывода. Например, новый открытый файл необходимо поставить в соответствие какому-либо потоку. Файлы можно открывать в режиме только для чтения, только для записи и в режиме чтения/записи. При записи в файл может возникнуть необходимость создать новый файл, заменить старый или добавить данные к старому файлу. Существует также возможность перемещаться по файлу в разных направлениях. Чтобы помочь справиться с этими задачами, в С++ определены несколько классов в заголовочном файле fstream.h, включая класс ifstream.h для выполнения файлового ввода и класс ofstream.h – для файлового вывода. С++ также определяет класс fstream.h для одновременного файлового ввода/вывода. Эти классы являются производными от классов в заголовочном файле iostream.h, поэтому объекты этих новых классов могут использовать те методы

Предположим, что программе необходимо записать информацию в файл. Для этого требуется выполнить следующее:1) Создать объект класса ofstream для управления потоком вывода. 2)Поставить этот объект в соответствие определенному файлу. 3)Использовать объект так же, как использовался объект cout; единственная разница заключается в том, что информация выводится в файл, а не на экран.

Для выполнения этой задачи начнем с включения заголовочного файла fstream.h. При включении этого файла автоматически включается файл iostream.h, поэтому iostream.h не нужно включать явно. Затем необходимо объявить объект класса ofstream:

ofstream fout; // создать объект класса ofstream с именем fout

Имя объекта может быть любым допустимым именем С++, например, fout, outFile, cgate и т.д.

Затем необходимо поставить в соответствие объекту определенный файл. Это можно сделать, воспользовавшись методом open(). Предположим, что необходимо открыть для записи файл, например, “file.txt”. Для этого нужно сделать следующее:

fout.open(“file.txt”); // поставить в соответствие объекту fout файл “file.txt”

Два этих этапа (создание объекта и его ассоциация с файлом) можно объединить в одном операторе, воспользовавшись другим конструктором:

ofstream fout (“file.txt”); // создать объект fout и поставить ему в соответствие файл “file.txt”

После этого объект fout (или объект с любым другим именем) можно использовать точно так же, как и объект cout. Например, если требуется записать слова New String в файл, то для этого необходимо выполнить следующее:

fout << “New String”;

В самом деле, поскольку класс ostream является базовым для класса ofstream, можно использовать методы класса ostream, включая различные определения операций вставки, а также методы и манипуляторы форматирования

4. Бинарные файлы в С++. Виды доступа к файлам.

Когда данные сохраняются в файле, их можно сохранить в текстовой форме или в двоичном формате. Текстовая форма означает, что все данные сохраняются как текст, даже числа. Например, сохранение значения 2.3456236e05 в текстовой форме означает сохранение 14 символов, из которых состоит данное число. Для этого требуется, чтобы внутреннее представление компьютера для числа с плавающей точкой было преобразовано в текстовую форму, с этой целью выполняется операция вставки <<. Двоичный формат означает, что число сохраняется во внутреннем представлении, т.е. вместо символов сохраняется 64-разрядное (обычно) представление числа типа double. Для символа двоичное представление совпадает с его текстовым – двоичным представлением ASCII-кода (или его эквивалента) символа. Однако для чисел двоичное представление очень сильно отличается от их текстового представления.

Каждый формат имеет свои достоинства. Текстовый формат прост для чтения, поэтому для чтения и редактирования текстового файла можно воспользоваться любым редактором. В двоичном же формате числа сохраняются более точно, поскольку он позволяет сохранить точное внутреннее представление числа. Не происходит ошибок преобразования или округления. Сохранение данных в двоичном формате может происходить быстрее, поскольку при этом не происходит преобразования и данные можно сохранять большими блоками. Кроме того, двоичный формат обычно занимает меньше места (в зависимости от природы данных).

В следующем примере формируется двоичный файл структур.

Пример.

// filebin.cpp - запись информации в бинарный файл

#include <iostream.h>

#include <fstream.h>

#include <stdlib.h> //для функции exit()

void main()

{struct person

{char name[20];// фамилия человека

int age; // возраст

char address[40];// домашний адрес};

person p1;// вывести начальное содержимое файла

char namefile[20];

ifstream fin;

fin.open("persons.dat",ios::in|ios::binary); //двоичный файл

if (fin.is_open())

{cout<<"The current contents of the "<<"persons.dat"<<" file:\n";

while (fin.read((char *) &p1, sizeof p1))

{cout<<p1.name<<":"<<p1.age<<": <<p1.address<<"\n";}}

fin.close();//добавление новых данных

ofstream fout;

fout.open("persons.dat", ios::out|ios::app|ios::binary);

if (! fout.is_open())

{cerr<<"Can't open "<<"persons.dat"<<" file for output:\n";

exit(1);}

cout<<"Enter person name (enter a blank line to quit):\n";

cin.get(p1.name,20);

while (p1.name[0] != '\0')

{while (cin.get()!='\n') continue;

cout<<"Enter person age: ";

cin>>p1.age;

cout<<"Enter person address: ";

cin>>p1.address;

fout.write((char *) &p1, sizeof p1);

cout<<"Enter person name (enter a blank line to quit):\n";

cin.get(p1.name,20);}

5. Функци в С++. Примеры.

Функция - это совокупность объявлений, и операторов, обычно предназначенная для решения определенной задачи. Каждая функция должна иметь имя, которое используется для ее объявления , определения и вызова. В любой программе на СИ должна быть функция с именем main (главная функция), именно с этой функции, в каком бы месте программы она не находилась, начинается выполнение программы.

При вызове функции ей при помощи аргументов (формальных параметров) могут быть переданы некоторые значения (фактические параметры) используемые во время выполнения функции. Функция может возвращать некоторое (одно !) значение. Это возвращаемое значение и есть результат выполнения функции, который при выполнении программы подставляется в точку вызова функции, где бы этот вызов не встретился. Допускается также использовать функции без аргументов и функции не возвращающие никаких значений. Действие таких функций может состоять, например, в изменении значений некоторых переменных, выводе на печать некоторых текстов и т.п..

С использованием функций в языке СИ связаны три понятия - определение функции (описание действий, выполняемых функцией), объявление функции (задание формы обращения к функции) и вызов функции (выполнение).

тип_результата имя_функции(тип_аргумента _имя_аргумента[, ...])

{ тело функции}

Функция может возвращать значения типа void, int,float и т.д. Имя функции может быть произвольным, но желательно, чтобы оно указывало на ее назначение. Если для выполнения функции нужно предоставить ей некоторую информацию в виде аргумента, то в круглых скобках должен быть указан тип аргумента и, при необходимости, его имя. Тип аргумента может быть любым. Если аргументов несколько, их описания (тип плюс имя) разделяются запятыми.

Пример:

int rus (unsigned char r)

{ if (r>='А' && c<='ё')

return 1;

else

return 0; }

В данном примере определена функция с именем rus, имеющая один параметр с именем r и типом unsigned char. Функция возвращает целое значение, равное 1, если параметр функции является буквой русского алфавита, или 0 в противном случае.

В языке СИ нет требования, чтобы определение функции обязательно предшествовало ее вызову. Определения используемых функций могут следовать за определением функции main, перед ним, или находится в другом файле.

Существуют два способа вызова функции:

имя_функции (е1, е2,. .., eN)

(указатель _на_функцию) (е1, е2,..., eN) Указатель_на_функцию - это переменная, содержащая адрес функции. Адрес функции может быть присвоен указателю оператором

указатель_на_функцию = имя _функции;

/* Неправильное использование параметров */

void change (int x, int y)

{ int k=x; x=y; y=k; }

В данной функции значения переменных x и y, являющихся формальными параметрами, меняются местами, но поскольку эти переменные существуют только внутри функции change, значения фактических параметров, используемых при вызове функции, останутся неизменными. Для того чтобы менялись местами значения фактических аргументов можно использовать функцию приведенную в следующем примере.

/* Правильное использование параметров */

void change (int *x, int *y)

{ int k=*x; *x=*y; *y=k; }

При вызове такой функции в качестве фактических параметров должны быть использованы не значения переменных, а их адреса change (&a,&b). Указатель на функцию может быть передан в качестве параметра функции. При этом разадресация происходит во время вызова функции, на которую ссылается указатель на функцию. Присвоить значение указателю на функцию можно в операторе присваивания, употребив имя функции без списка параметров.

double (*fun1)(int x, int y);

double fun2(int k, int l);

fun1=fun2; /* инициализация указателя на функцию */

(*fun1)(2,7); /* обращение к функции */

fout.close();// вывести измененный файл

fin.open("persons.dat", ios::in|ios::binary);

if (fin.is_open())

{cout<<"The new contents of the "<<"persons.dat"<<"file:\n";

while (fin.read((char *) &p1, sizeof p1))

{cout<<p1.name<<": "<<p1.age<<": "<<p1.address<<"\n";}}

fin.close();}

Режим файла описывает, как используется файл: для чтения, для записи, для добавления и т.д. Когда поток ассоциируется с файлом, а также при инициализации файлового потокового объекта именем файла или при работе с методом open(), можно использовать и второй аргумент, задающий режим файла:

// конструктор с аргументом режима

ifstream fin(“file_in”, mode1);

ofstream fout();

// метод open() с аргументом режима

fout.open(“file_out”, mode2);

Класс ios_base определяет тип openmode для представления режима файла; это тип битовой маски. В классе ios_base определено несколько констант, которыми можно воспользоваться для указания режима файла. Константы режимов файлов

Константа

Значение

ios_base :: in

Открыт файл для чтения

ios_base :: out

Открыть файл для записи

ios_base :: ate

Переместить указатель в конец файла после его открытия

ios_base :: app

Добавить информацию к концу файла

ios_base :: trunc

Урезать файл, если он существует

ios_base :: binary

Двоичный файл

Конструкторы классов ifstream, ofstream, а также методы open() имеют по два аргумента, однако могут использоваться с одним аргументом, так как прототипы для этих функций-элементов класса обеспечивают для второго аргумента (аргумента режима файла) значения, заданные по умолчанию

ofstream fout(“file_out”, ios_base::out | ios_base::app);

Режимы открытия файлов С++

Режим

Значение

ios_base :: in

Открыт файл для чтения.

ios_base :: out

(То же самое, что и ios_base::out | ios_base::trunc).

ios_base::out | ios_base::trunc

Открыть для записи, урезав длину файла до нуля, если он уже существует.

ios_base::out | ios_base::app

Открыть файл для записи, но только для добавления к файлу.

ios_base::in | ios_base::out

Открыть для чтения и записи, причем запись разрешена в любом месте файла

ios_base::in | ios_base::out | ios_base::trunc

Открыть файл для чтения и записи, вначале урезав длину файла до нуля, если он уже существует.

Класс ofstream работает с буферизованным выводом, поэтому программа выделяет место для буфера вывода при создании такого объекта класса ofstream, как fout. Если создаются два объекта класса ofstream, то программа создает два буфера – отдельно для каждого объекта. Объект класса ofstream наподобие fout считывает побайтно вывод, сгенерированный программой. Затем, когда буфер заполняется, содержимое буфера целиком передается в файл назначения.

Открытие файла для вывода таким способом позволяет создать новый файл, если файла с таким именем не существует. Если же файл с таким именем существует, то до открытия для вывода этот файл урезается до нулевого размера и информация начинает выводиться в пустой файл.

Замечание. При открытии существующего файла для вывода (записи) в режиме, заданном по умолчанию, автоматически урезается его размер до нуля и удаляется его предыдущее содержимое.

Требования к чтению файла очень похожи на требования к записи:

  1. Создать объект класса ifstream для управления потоком ввода. 2.Поставить этот объект в соответствие определенному файлу. 3.Использовать объект так же, как объект cin.

Этапы выполнения этих действий совпадают с этапами записи файла. Сначала конечно же необходимо включить заголовочный файл fstream.h, затем объявить объект класса ifstream и поставить ему в соответствие файл. Это можно сделать с помощью двух операторов или одного:

ifstream fin; // два оператора // создать объект класса ifstream с именем fin

fin.open(“example.txt”); // открыть файл, например, example.txt для чтения

ifstream fin(“example.txt”); // один оператор // создать объект fin и поставить ему в соответствие файл example.txt

Теперь объект fin можно использовать так же, как объект cin. Например, можно сделать следующее:

char ch;

fin >> ch; // считать символ из файла example.txt

char buf [80];

fin >> buf; // считать слово из файла

fin.getline(buf, 80); // считать строку из файла

Ввод, как и вывод, буферизован, поэтому при создании объекта класса ifstream наподобие fin создается буфер ввода, управляемый этим объектом.

Связь с файлом прекращается автоматически, если истекает срок действия объектов потока ввода/вывода, например, по окончании выполнения программы. Кроме того, связь с файлом можно прервать явно, воспользовавшись методом close():

fout.close(); // прервать связь с файлом для вывода

fin.close(); // прервать связь с файлом для ввода

предусмотренные в цикле действия не должны выполниться ни одного раза. Этот оператор цикла имеет вид

While <условие> {тело цикла};

Если логическое выражение, задающее <условие>, имеет значение TRUE, то выполняется тело цикла, после чего повторяется проверка условия. Если <условие> имеет значение FALSE, оператор While прекращает свою работу. Поскольку условие завершения циклического процесса проверяется до выполнения тела цикла, то оператор цикла данного вида называется оператором цикла с предусловием.

3. Оператор цикла с параметром. Используется тогда, когда число повторений цикла известно к началу его повторения, и управление циклом осуществляется с помощью переменной скалярного типа, которая в этом циклическом процессе принимает последовательные значения от заданного начального до конечного значения. Паскаль допускает два вида этого оператора:

For (V=E1;E1<= E2; E1++)

{ тело цикла};

где for , to , do - кодовые слова, V - переменная порядкового типа, V - параметр цикла; E1, E2 - выражения того же типа, что и V. При выполнении оператора цикла с параметром предусматривается присваивание параметру цикла V последовательных значений от начального, равного значению выражения E1, до конечного, равного значению выражения E2, и выполнение тела цикла при каждом значении параметра цикла V. При этом значения выражений E1 и E2 вычисляются один раз, при входе в оператор цикла, а значение параметра V не должно изменяться в результате выполнения тела цикла. Если заданное конечное значение параметра цикла меньше начального, то тело цикла не выполняется ни разу.

Оператор continue , как и оператор break, используется только внутри операторов цикла for, while или do while. Этот оператор позволяет продолжить выполнение цикла, пропустив операторы оставшиеся в теле цикла. Обычно этот оператор входит в состав оператора if. Формат оператора: continue;

Пример:

int main ( )

{ int a,b;

for ( a=1, b=0 ; a<100; b+=a, a++)

{ if (b%2) continue;

... /* обработка четных сумм */ }

return 0; }

Когда сумма чисел от 1 до а становится нечетной, оператор continue передает управление на очередную итерацию цикла for, не выполняя операторы обработки четных сумм.

Оператор continue, как и оператор break, прерывает самый внутренний из объемлющих его циклов.

Использование оператора безусловного перехода goto в практике программирования на языке Си настоятельно не рекомендуется, так как он затрудняет понимание программ и возможность их модификаций.

Формат этого оператора следующий: goto метка;

Оператор goto передает управление на оператор, помеченный меткой метка. Помеченный оператор должен находиться в той же функции, что и оператор goto, а метка должна быть уникальной, т.е. нельзя помечать одной меткой более одного оператора.

Оператор break может быть использован только внутри операторов switch, for, while или do while и обеспечивает прекращение выполнения самого внутреннего из содержащих его операторов. После выполнения оператора break управление передается оператору следующему за прерванным.

Формат оператора: break;

Заметим, что оператор break нельзя использовать для выхода из нескольких вложенных циклов, а составной оператор, состоящий из двух операторов break, эквивалентен одному оператору break.

Однако здесь есть некоторая асимметрия: всплывает пузырек сразу, за один проход, а тонет очень медленно, за один проход на одну позицию. Например, массив 12 18 42 44 55 67 94 6 с помощью усовершенствованной “пузырьковой” сортировки можно упорядочить за один просмотр, а для сортировки массива 94 6 12 18 42 44 55 67 требуется 7 просмотров. Такая неестественная симметрия наводит на мысль о третьем улучшении : чередовать направление последовательных просмотров. Получающийся при этом алгоритм называют “шейкернойсортировкой (ShakerSort).

4. Быстрая сортировка

Самый лучший из известных в данный момент метод сортировки для массивов. Его изобретатель Ч. Хоар назвал метод быстрой сортировкой (QuickSort).

Алгоритм. Выберем наугад какой-либо элемент (назовем его x) и будем просматривать слева наш массив до тех пор, пока не обнаружим элемент ai>x, затем будем просматривать массив справа, пока не встретим aj<x. Теперь Поменяем местами эти два элемента и продолжим наш процесс просмотра и обмена , пока оба просмотра не встретятся где-то в середине массива. В результате массив окажется разбитым на левую часть, с ключами меньше (или равными) x, и правую – с ключами больше (или равными) x.

Наша цель – не только провести разделение на части исходного массива элементов, но и отсортировать его. Сортировку от разделения отделяет лишь небольшой шаг: нужно применить этот процесс к получившимся двум частям, затем к частям частей, и так до тех пор, пока каждая из частей не будет состоять из одного-единственного элемента.

Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]