Опубликован: 05.01.2015 | Доступ: свободный | Студентов: 2065 / 0 | Длительность: 63:16:00
Лекция 6:

Элементарные методы сортировки

Сортировка других типов данных

Большинство алгоритмов сортировки вполне можно рассматривать как упорядочение массивов чисел в числовом порядке или упорядочение символов в алфавитном порядке. Действительно, эти алгоритмы обычно не зависят от типа сортируемых элементов, и поэтому их нетрудно распространить на более общие случаи. Ранее мы подробно рассмотрели вопросы разбиения программ на отдельные независимые модули, что позволяет реализовать нужные типы данных, а также абстрактные типы данных (см. "Элементарные структуры данных" и "Абстрактные типы данных" ). В данном разделе обсуждаются способы применения этих понятий для создания реализаций, интерфейсов и клиентских программ для алгоритмов сортировки. А именно, будут рассматриваться интерфейсы для:

  • элементов или обобщенных сортируемых объектов
  • массивов элементов.

Тип данных " элемент " позволяет использовать программы сортировки для любых типов данных, для которых определены необходимые базовые операции. Такой подход позволяет эффективно создавать реализации как для простых, так и для абстрактных типов данных. Интерфейс " массив " менее критичен для нашей задачи; мы используем его в качестве примера работы с многомодульной программой, которая имеет дело с несколькими типами данных. Здесь будет рассмотрена только одна (примитивная) реализация интерфейса массива.

Программа 6.6 является клиентской программой с теми же обобщенными возможностями, что и функция main из программы 6.1, но с дополнительным кодом для работы с массивами и элементами, инкапсулированными в отдельных модулях. Это позволяет, в частности, тестировать различные программы сортировки на различных типах данных путем замены одних модулей на другие, не внося при этом никаких изменений в клиентскую программу. Программа 6.6 обращается также к интерфейсу, где описаны операции exch и compexch, используемые в реализациях алгоритмов сортировки. Можно было бы включить их в интерфейс Item.h, однако реализации в программе 6.1 имеют легко понятную семантику при определенных операциях = и <, поэтому проще содержать их в одном модуле, которым могут пользоваться все реализации сортировки для всех типов элементов. Чтобы завершить реализацию, необходимо вначале точно определить интерфейсы с типами массива и элемента.

Программа 6.6. Драйвер сортировки массивов

Данный драйвер для основных алгоритмов сортировки массивов использует три явных интерфейса: (1) для типа данных, который инкапсулирует операции для обобщенных элементов; (2) несколько более высокий уровень для функций exch и compexch, используемых в реализациях; и (3) для функций, которые инициализируют и выводят (а также сортируют!) массивы. Такое разбиение драйвера на модули позволяет без каких-либо изменений применять каждую реализацию сортировки для упорядочения различных типов данных, совместно использовать реализации операций обмена и сравнения-обмена, а также компилировать функции для массивов отдельно (возможно, для их использования в других драйверах).

#include <stdlib.h>
#include "Item.h"
#include "exch.h"
#include "Array.h"
main(int argc, char *argv[])
  { int N = atoi(argv[1]), sw = atoi(argv[2]);
    Item *a = new Item[N];
    if (sw) rand(a, N); else scan(a, N);
    sort(a, 0, N-1);
    show(a, 0, N-1);
  }
      

Интерфейс программы 6.7 определяет примеры высокоуровневых операций, которые могут понадобиться при работе с массивами. Необходима возможность инициализировать массив либо случайными ключами, либо ключами из стандартного ввода, необходима возможность сортировать элементы (естественно!), и необходима возможность вывода содержимого массива. Это лишь несколько примеров; в конкретном приложении может понадобиться определить и другие операции (примером подобного интерфейса может служить класс Vector из библиотеки стандартных шаблонов). Программа 6.7 позволяет подставлять различные реализации разных операций без внесения изменений в клиентскую программу, которая использует этот интерфейс — в данном случае, в функцию main программы 6.6. Реализациями функции sort могут служить различные рассматриваемые нами реализации сортировок. В программе 6.8 приведены простые реализации других функций. Модульная организация позволяет подставлять другие реализации, нужные в конкретных приложениях. Например, может понадобиться реализация функции show, которая выводит только часть массива при тестировании работы на очень больших массивах.

Подобным же образом, чтобы иметь возможность работать с конкретными типами элементов и ключей, мы определим их типы и объявим все необходимые операции над ними в отдельном интерфейсе, а затем приведем реализации этих операций, определенных в этом интерфейсе. Например, пусть имеется некоторое бухгалтерское приложение, где целочисленный ключ соответствует номеру счета клиента, а число с плавающей точкой — балансу счета этого клиента. Программа 6.9 представляет собой пример интерфейса, который определяет тип данных для подобных приложений. Код этого интерфейса объявляет операцию <, которая нужна для сравнения ключей, а также функции, которые генерируют случайные ключи, считывают ключи и выводят значения ключей. В программе 6.10 содержатся реализации функций для данного простого примера. Разумеется, эти реализации можно приспособить под конкретные приложения.

Программа 6.7. Интерфейс для типа данных " массив "

Данный интерфейс Array.h определяет высокоуровневые функции для массивов абстрактных элементов: инициализация случайными значениями, инициализация значениями из стандартного ввода, вывод содержимого и сортировку содержимого.

template <class Item>
  void rand(Item a[], int N);
template <class Item>
  void scan(Item a[], int &N);
template <class Item>
  void show(Item a[], int l, int r);
template <class Item>
  void sort(Item a[], int l, int r);
      

Программа 6.8. Реализация типа данных " массив "

Данный код представляет собой реализацию функций, определенных в программе 6.7. Здесь используются типы данных и базовые функции для работы с ними, которые определены в отдельном интерфейсе (см. программу 6.9).

#include <iostream.h>
#include <stdlib.h>
#include "Array.h"
template <class Item>
  void rand(Item a[], int N)
    { for (int i = 0; i < N; i++) rand(a[i]); }
template <class Item>
  void scan(Item a[], int &N)
    {
      for (int i = 0; i < N; i++)
        if (!scan(a[i])) break;
      N = i;
    }
template <class Item>
  void show(Item a[], int l, int r)
    { for (int i = l; i <=r; i++)
      show(a[i]);
      cout << endl;
    }
      

Программа 6.9. Пример интерфейса для типа данных " элемент "

Файл Item.h, включенный в программу 6.6, дает определение типа данных для сортируемых элементов. В этом примере элементами являются небольшие записи, состоящие из целочисленных ключей и информации в виде числа с плавающей точкой. Мы объявляем, что перегруженная операция < будет реализована отдельно, равно как и три функции: scan (считывает Item в свой аргумент), rand (сохраняет случайный Item в своем аргументе) и show (выводит Item).

typedef struct record { int key; float info; } Item;
  int operator<(const Item&, const Item&);
int scan(Item&);
void rand(Item&); void show(const Item&);
      

Программа 6.10. Пример реализации типа данных " элемент "

Этот код является реализацией перегруженной операции < и функций scan, rand и show, которые объявлены в программе 6.9. Поскольку записи представляют собой небольшие структуры, в функции exch можно использовать встроенный оператор присваивания, не беспокоясь о затратах на копирование элементов.

#include <iostream.h>
#include <stdlib.h>
#include "Item.h"
int operator<(const Item& A, const Item& B)
  { return A.key < B.key; }
int scan(Item& x)
  { return (cin >> x.key >> x.info) != 0; }
void rand(Item& x)
  { x.key = 100 0*(1.0*rand()/RAND MAX);
  x.info = 1.0*rand()/RAND MAX; }
void show (const Item& x)
  { cout << x.key << " " << x.info << endl; }
      

Например, тип Item может быть абстрактным типом данных, определенным в виде класса С++, а ключи могут быть функциями-членами класса, а не членами структуры. Такие АТД рассматриваются в "Таблицы символов и деревья бинарного поиска" .

Программы 6.6—6.10 вместе с любыми подпрограммами сортировки (без изменений) из разделов 6.2—6.6, выполняют проверку сортировки на небольших записях. Написав подобные интерфейсы и реализации для других типов данных, мы сможем применять наши реализации для сортировки различных видов данных — таких как комплексные числа (см. упражнение 6.50), векторы (см. упражнение 6.55) или полиномы (см. упражнение 6.56) — без каких-либо изменений в кодах программ сортировки. Для более сложных типов элементов придется написать более сложные интерфейсы и реализации, однако эта задача полностью отделена от вопросов построения алгоритмов сортировки, которые нас здесь интересуют. Одни и те же механизмы можно задействовать для большинства методов сортировки, которые рассмотрены в данной главе, а также будут изучаться в лекциях 7—9 . В разделе 6.10 мы подробно проанализируем одно существенное исключение — оно дает начало целому семейству важных алгоритмов сортировки, которые должны иметь совсем другое оформление; о них речь пойдет в "Поразрядная сортировка" .

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

Упражнения

6.49. Напишите версии программ 6.9 и 6.10, в которых вместо функций scan и show используются перегруженные операции << и >>, а также измените программу 6.8, чтобы учесть изменения в интерфейсе.

6.50. Напишите интерфейс и реализацию для обобщенного типа данных элемента, который позволит сортировать комплексные числа х + iy, используя в качестве ключа модуль $\sqrt{x^{2}+y^{2}}$. Совет: эффективность можно повысить, игнорируя квадратный корень.

6.51. Напишите интерфейс, который определяет абстрактный тип данных первого класса для обобщенных элементов (см. раздел 4.8 "Абстрактные типы данных" ), и реализацию, в которой, как и в предыдущем упражнении, элементами являются комплексные числа. Протестируйте полученную программу с помощью программ 6.3 и 6.6.

6.52. Добавьте в тип данных массива в программах 6.8 и 6.7 функцию check, которая проверяет, упорядочен ли массив.

6.53. Добавьте в тип данных массива в программах 6.8 и 6.7 функцию testinit, которая генерирует тестовые данные с распределениями, похожими на приведенные на рис. 6.13. У этой функции должен быть целочисленный аргумент, через который клиентская программа сможет задать нужное распределение.

6.54. Измените программы 6.7 и 6.8, чтобы реализовать абстрактный тип данных. (Ваша реализация должна размещать массив в памяти и сопровождать его так, как в реализациях стеков и очередей из "Элементарные структуры данных" )

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

6.56. Напишите интерфейс и реализацию для обобщенного типа данных элемента, чтобы известные методы сортировки могли упорядочивать полиномы (см. раздел 4.9 "Абстрактные типы данных" ). Правило упорядочивания определите самостоятельно.

Александра Боброва
Александра Боброва

Я прошла все лекции на 100%.

Но в https://www.intuit.ru/intuituser/study/diplomas ничего нет.

Что делать? Как получить сертификат?

Никита Андриянов
Никита Андриянов
Владимир Хаванских
Владимир Хаванских
Россия, Москва, Высшая школа экономики
Вадим Рычков
Вадим Рычков
Россия, Москва, МГТУ Станкин