ФЕДЕРАЛЬНОЕ АГЕНТСТВО ПО ОБРАЗОВАНИЮ Федеральное государственное образовательное учреждение высшего профессионального об...
17 downloads
190 Views
509KB Size
Report
This content was uploaded by our users and we assume good faith they have the permission to share this book. If you own the copyright to this book and it is wrongfully on our website, we offer a simple DMCA procedure to remove your content from our site. Start by pressing the button below!
Report copyright / DMCA form
ФЕДЕРАЛЬНОЕ АГЕНТСТВО ПО ОБРАЗОВАНИЮ Федеральное государственное образовательное учреждение высшего профессионального образования «ЮЖНЫЙ ФЕДЕРАЛЬНЫЙ УНИВЕРСИТЕТ»
С.С. Михалкович
Основы программирования Динамические массивы. Списки. Ассоциативные массивы. Деревья. Хеш-таблицы
МЕТОДИЧЕСКИЕ УКАЗАНИЯ для студентов 1 курса факультета математики, механики и компьютерных наук
Ростов-на-Дону 2007 3
Аннотация Методические указания содержат лекции по темам «Контейнерные классы», «Деревья», курса «Основы программирования» для студентов направления «Информационные технологии» факультета математики, механики и компьютерных наук Методические указания разработаны кандидатом физико-математических наук, доцентом кафедры алгебры и дискретной математики Михалковичем С.С.
Печатается в соответствии с решением кафедры алгебры и дискретной математики факультета математики, механики и компьютерных наук ЮФУ, протокол № 3 от 13 ноября 2006 г. 4
1 Класс «Динамический массив» Сформулируем основные требования к классу «Динамический массив». Объекты этого класса должны вести себя как массивы, т.е. представлять собой набор элементов, доступ к которым осуществляется по индексу. Размер такого массива задается при создании объекта и может меняться в процессе работы программы. Договоримся также, что индексация будет производиться с нуля. Как и в случае класса «Очередь», будем обозначать тип элементов динамического масссива через DataType. Например, динамический массив целых поместим в модуль IntArray и положим DataType=integer. Класс динамического массива назовем DynArray. При использовании в программе динамических массивов с элементами разных типов будем уточнять имя типа именем модуля: IntArray.DynArray или RealArray.DynArray. 1.1 Предварительный интерфейс класса «Динамический массив» Составим предварительный вариант интерфейса класса DynArray: type DynArray=class public constructor Create(n: integer); destructor Destroy; procedure Resize(newsize: integer); procedure Reserve(newcap: integer); function Size: integer; function Capacity: integer; procedure Add(x: DataType); procedure TrimToSize; function GetItem(i: integer): DataType; procedure SetItem(i: integer; x: DataType); end; Конструктор Create создает динамический массив с n элементами, индексируемый от нуля, деструктор Destroy разрушает его. Функция Size возвращает количество элементов в массиве, процедура Resize устанавливает количество элементов равным newsize, а процедура Add добавляет элемент x в конец массива, увеличивая размер массива на 1. Функция GetItem(i) возвращает значение i-го элемента массива, а процедура SetItem(i,x) устанавливает значение i-го элемента массива равным x; если индекс i находится вне диапазона 0..Size-1, то генерируется исключение. Как уже говорилось, размер динамического массива может постоянно меняться по ходу работы программы. Например, при добавлении элемента размер увеличивается на 1. Задумаемся, однако, как должен быть реализован метод Re5
size. Рассмотрим два противоположных подхода. Первый подход заключается в следующем: под массив отводится максимально возможная память, а при изменении размера меняется только значение закрытого поля, хранящего текущий размер массива. Это решение – эффективное по скорости, но расточительное по памяти. Второй подход – всякий раз выделяется память, размер которой совпадает с размером массива. Такое решение эффективно по памяти, но крайне неэффективно по скорости работы. Действительно, при каждом изменении размера надо создавать новый участок памяти, копировать в него содержимое старого массива и освобождать старый участок памяти. К примеру, если массив состоит из 1000 элементов, то при добавлении одного элемента необходимо выделить память под 1001 элемент, скопировать 1000 элементов в эту память и освободить старую память. Как известно, истина находится посередине между полярными мнениями. Обычно выбирается следующая стратегия изменения размера динамического массива. В начальный момент времени под массив отводится память, превосходящая его размер. Если при выполнении Resize новый размер не превосходит выделенную память, то просто меняется внутреннее поле, характеризующее размер. В противном случае выделяется новая память, происходит копирование старого содержимого в новую память и старое содержимое освобождается. Размер выделяемой памяти должен превосходить новый размер массива с прицелом на дальнейшее увеличение. Обычно принимают следующую стратегию: выделяют память, в 2 раза превосходящую запрашиваемый размер. Назовем метод, ответственный за выделение памяти, именем Reserve. Отметим, что выделенная память не инициализируется нулями, поэтому за ее инициализацию ответственна клиентская программа. Таким образом, динамический массив характеризуется не только своим размером Count, но и так называемой емкостью Capacity, определяющей количество элементов, под которые выделена память. В любой момент времени Size0’); if newcap=0,’Размер массива не может быть cap then 8
Reserve(2*sz); end; function DynArray.Size: integer; begin Result:=sz; end; function DynArray.Capacity: integer; begin Result:=cap; end; procedure TrimToSize; var data1: PIntArr; begin cap:=sz; if cap=0 then cap:=4; GetMem(data1,cap*sizeof(DataType)); Move(data,data1,sz*sizeof(DataType)); FreeMem(data); end; procedure Add(x: DataType); begin Resize(Count+1); data^[Count-1]:=x; end; procedure DynArray.SetItem(i: integer; x: DataType); begin Assert((i>=0) and (i<sz), ’Выход за границы массива ’+IntToStr(i)); data^[i]:=x; end; function DynArray.GetItem(i: integer): DataType; begin Assert((i>=0) and (i<sz), ’Выход за границы массива: ’+IntToStr(i)); Result:=data^[i]; end; end. Поясним реализацию некоторых методов. Класс DynArray имеет три перегруженных конструктора, объявленых с использованием зарезервированного слова overload. В первом конструкторе массива указывается его первоначальный 9
размер и емкость. Если емкость меньше размера, то она устанавливается равной размеру, а если размер равен 0, то емкость полагается равной 4 элементам. Второй конструктор с одним параметром создает динамический массив с емкостью, равной размеру. Он предназначен для массивов, для которых маловероятно дальнейшее увеличение размера. Наконец, третий конструктор (без параметров) создает динамический массив с нулевым размером. Предполагается, что размер такого массива станет известен далее при выполнении программы. Следует обратить внимание, что во втором конструкторе первый конструктор вызывается как обычная процедура. Здесь проявляется двойственная природа конструктора: при конструировании объекта он вызывается как функция, возвращающая сконструированный объект, а если объект создан, то конструктор может быть вызван как обычная процедура для выполнения инициализации полей объекта. В методах Resize и TrimToSize под содержимое массива выделяется новая память, и старое содержимое копируется в нее при помощи стандартной процедуры Move. Помимо указанного набора методов, в класс DynArray уместно также включить следующие методы: function ToString: AnsiString; function IndexOf(x: DataType): integer; function LastIndexOf(x: DataType): integer; procedure Sort; procedure Reverse; procedure ForEach(p: Proc); Функция ToString преобразует содержимое массива к строковому представлению. Функции IndexOf и LastIndexOf возвращают соответственно индекс первого и последнего элемента, равного x, а если элемент не найден, то возвращается -1. Процедура Sort сортирует элементы массива по возрастанию, используя для сравнения операцию