Алгоритм простого перебора
Ниже приведен текст программы поиска в массиве целых чисел. Перебор элементов массива осуществляется инструкцией repeat, в теле которой инструкция if сравнивает текущий элемент массива с образцом и присваивает переменной found значение true, если текущий элемент и образец равны.
Цикл завершается, если в массиве обнаружен элемент, равный образцу (в этом случае значение переменной found равно true), или если проверены все элементы массива. По завершении цикла по значению переменной found можно определить, успешен поиск или нет.
Вид диалогового окна программы Поиск в массиве приведен на рис. 5.9.
Рис. 5.9. Диалоговое окно программы Поиск в массиве
Щелчок на командной кнопке Поиск (Buttoni) запускает процедуру TForm1.Button1Click (ее текст приведен в листинге 5.7), которая из компонента stringGridi вводит массив, а из поля редактирования Edit2 — число (образец). Затем выполняется проверка, содержит ли массив введенное число. После завершения проверки процедура showMessage выводит сообщение о результате поиска.
Листинг 5.7. Поиск в массиве
unit s_found_; interface
uses
Windows, Messages, SysUtils, Classes,
Graphics, Controls, Forms, Dialogs,
StdCtrls, Grids;
type
TForm1 = class(TForm)
Label1: TLabel;
Label2: TLabel;
Button1: TButton;
Edit2: TEdit;
StringGridi: TStringGrid;
procedure ButtonlClick(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations )
end;
var
Form1: TForm1 ;
implementation
{$R *.DFM}
{ поиск в массиве перебором }
procedure TForml.ButtonlClick(Sender: TObject);
const
SIZE=5; var
a: array[1..SIZE] of integer; //массив
obr: integer; // образец для поиска
found: boolean; // TRUE — совпадение образца с элементом
// массива
i: integer; // индекс элемента массива
begin
// ввод массива for i:=l to SIZE do
a[i] := StrToInt(StringGridl.Cells[i-1,0]);
// ввод образца для поиска
obr := StrToInt(edit2.text);
// поиск
found := FALSE; // пусть нужного элемента в массиве нет
i:= 1;
repeat
if a[i] = obr
then found := TRUE else i := i+1;
until (i > SIZE) or (found = TRUE);
if found
then ShowMessage('Совпадение с элементом номер '
+IntToStr(i)+#13+'Поиск успешен.')
else ShowMessage('Совпадений с образцом нет.');
end;
end.
Очевидно, что чем больше элементов в массиве и чем дальше расположен нужный элемент от начала массива, тем дольше программа будет искать необходимый элемент.
Поскольку операции сравнения применимы как к числам, так и к строкам, данный алгоритм может использоваться для поиска как в числовых, так и в строковых массивах.
Использование компонента StringGrid
Для ввода массива удобно использовать компонент StringGrid. Значок компонента StringGrid находится на вкладке Additional (рис. 5.2).
Рис. 5.2. Компонент StringGrid
Компонент StringGrid представляет собой таблицу, ячейки которой содержат строки символов. В табл. 5.1 перечислены некоторые свойства компонента StringGrid.
Таблица 5.1. Свойства компонента StringGrid
Свойство |
Определяет |
||
Name |
Имя компонента. Используется в программе для доступа к свойствам компонента |
||
Свойство |
Определяет |
||
ColCount |
Количество колонок таблицы |
||
RowCount |
Количество строк таблицы |
||
Cells |
Соответствующий таблице двумерный массив. Ячейка таблицы, находящаяся на пересечении столбца номер col и строки номер row определяется элементом cells [col, row] |
||
FixedCols |
Количество зафиксированных слева колонок таблицы. Зафиксированные колонки выделяются цветом и при горизонтальной прокрутке таблицы остаются на месте |
||
FixedRows |
Количество зафиксированных сверху строк таблицы. Зафиксированные строки выделяются цветом и при вертикальной прокрутке таблицы остаются на месте |
||
Options . goEditing |
Признак допустимости редактирования содержимого ячеек таблицы. True — редактирование разрешено, False — запрещено |
||
Options . goTab |
Разрешает (True) или запрещает (False) использование клавиши <Таb> для перемещения курсора в следующую ячейку таблицы |
||
Options . GoAlways-ShowEditor |
Признак нахождения компонента в режиме редактирования. Если значение свойства False, то для того, чтобы в ячейке появился курсор, надо начать набирать текст, нажать клавишу <F2> или сделать щелчок мышью |
||
DefaultColWidth |
Ширину колонок таблицы |
||
DefaultRowHeight |
Высоту строк таблицы |
||
GridLineWi-dth |
Ширину линий, ограничивающих ячейки таблицы |
||
Left |
Расстояние от левой границы поля таблицы до левой границы формы |
||
Top |
Расстояние от верхней границы поля таблицы до верхней границы формы |
||
Height |
Высоту поля таблицы |
||
Width |
Ширину поля таблицы |
||
Font |
Шрифт, используемый для отображения содержимого ячеек таблицы |
||
ParentFont |
Признак наследования характеристик шрифта формы |
||
В качестве примера использования компонента stringGrid для ввода массива рассмотрим программу, которая вычисляет среднее арифметическое значение элементов массива. Диалоговое окно программы приведено на рис. 5.3. Компонент stringGrid используется для ввода массива, компоненты Label1 и Label2 — для вывода пояснительного текста и результата расчета, Buttoni — для запуска процесса расчета.
Рис. 5.3. Диалоговое окно программы Ввод и обработка массива
Добавляется компонент stringGrid в форму точно так же, как и другие компоненты. После добавления компонента к форме нужно выполнить его настройку в соответствии с табл. 5.2. Значения свойств Height и width следует при помощи мыши установить такими, чтобы размер компонента был равен размеру строки.
Текст программы приведен в листинге 5.2.
Таблица 5.2. Значения свойств компонента StringGrid1
Свойство |
Значение |
||
ColCount |
5 |
||
FixedCols |
0 |
||
RowCount |
1 |
||
DefaultRowHeight |
24 |
||
Height |
24 |
||
DefaultColWidth |
64 |
||
Width |
328 |
||
Options . goEditing |
True |
||
Options . AlwaysShowEditing |
True |
||
Options .goTabs |
True |
||
Листинг 5.2. Ввод и обработка массива целых чисел
unit getar_;
interface
uses
Windows, Messages, SysUtils, Variants,
Classes, Graphics, Controls, Forms, Dialogs, Grids, StdCtrls;
type
TForm1 = class(TForm)
Label1: TLabel;
StringGridl: TStringGrid;
Button1: TButton;
Label2: TLabel;
procedure ButtonlClick(Sender: TObject); private
{ Private declarations }
public
{ Public declarations }
end;
var
Form1: TForml ;
implementation
{$R *.dfm}
procedure TForml.ButtonlClick(Sender: TObject); var
a : array[1..5] of integer; // массив
summ: integer; // сумма элементов
sr: real; // среднее арифметическое
i: integer; // индекс
begin
// ввод массива
// считаем, что если ячейка пустая, то соответствующий
// ей элемент массива равен нулю
for i:= 1 to 5 do
if Length(StringGridl.Cells[i-1, 0]) <>0
then a[i] := StrToInt(StringGridl.Cells[i-1,0])
else a[i] := 0;
// обработка массива
summ := 0;
for i :=1 to 5 do
summ := summ + a[i]; sr := summ / 5;
У вывод результата Label2.Caption :=
'Сумма элементов: ' + IntToStr(summ)
+ #13+ 'Среднее арифметическое: ' + FloatToStr(sr);
end;
end.
После пробных запусков программы возникает желание внести изменения в процесс ввода массива. Так, было бы неплохо, чтобы курсор автоматически переходил в следующую ячейку таблицы, например, в результате нажатия клавиши <Enter>. Сделать это можно при помощи процедуры обработки события onKeyPress. На эту же процедуру можно возложить задачу фильтрации вводимых в ячейку таблицы данных. В нашем случае надо разрешить ввод в ячейку только цифр.
Текст процедуры обработки события OnKeyPress приведен в листинге 5.3. Следует обратить внимание на свойство Col, которое во время работы программы содержит номер колонки таблицы, в которой находится курсор. Это свойство можно также использовать для перемещения курсора в нужную ячейку таблицы. Однако нужно учитывать, что колонки таблицы, впрочем, как и строки, нумеруются с нуля.
Листинг 5.3. Процедура обработки события OnKeyPress
procedure TForm1.StringGridlKeyPress(Sender: TObject;
var Key: Char);
begin
case Key of
#8,'0'..'9' : ; // цифры и клавиша <Backspace>
#13: // клавиша <Enter>
if StringGridl.Col < StringGridl.ColCount — 1
then StringGridl.Col := StringGridl.Col + 1;
else key := Chr(0); // остальные символы запрещены
end;
end;
Если нужно ввести массив дробных чисел (a: array [1. .5] of real), то процедура обработки события OnKeyPress несколько усложнится, т. к. помимо цифр допустимыми символами являются символ-разделитель (запятая или точка — зависит от настройки Windows) и минус. С целью обеспечения некоторой дружественности программы по отношению к пользователю можно применить трюк: подменить вводимый пользователем неверный разделитель верным. Определить, какой символ-разделитель допустим в текущей настройке Windows, можно, обратившись к глобальной переменной Decimaiseparator.
В листинге 5.4 приведен текст модуля приложения ввода и обработки массива дробных чисел. Процедура обработки события OnKeyPress обеспечивает ввод в ячейку таблицы только допустимых при записи дробного числа символов.
Листинг 5.4. Ввод и обработка массива дробных чисел
unit. getar_1; interface
uses
Windows, Messages, SysUtils, Variants, Classes,
Graphics, Controls, Forms, Dialogs, Grids, StdCtrls;
type
TForm1= class(TForm)
Label1: TLabel;
StringGrid1: TStringGrid;
Button1: TButton;
Label2: TLabel;
procedure Button1ClicktSender: TObject);
procedure StringGridlKeyPress(Sender: TObject; var Key: Char);
private
{ Private declarations }
public
{ Public declarations }
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
procedure TForm1.ButtonlClick(Sender: TObject);
var
a : array[1..5] of real; // массив
suram: real; // сумма элементов
sr: real; // среднее арифметическое
i: integer; // индекс
begin
// ввод массива
// считаем, что если ячейка пустая, то соответствующий
// ей элемент массива равен нулю
for i:= 1 to 5 do
if Length(StringGridl.Cells[i-l,0])<>0
then a[i] := StrToFloat(StringGridl.Cells[i-1, 0]) else a[i] := 0;
// обработка массива
summ := 0;
for i :=1 to 5 do
summ := summ + a[i]; sr := summ / 5;
// вывод результата
Label2.Caption :=
'Сумма элементов: ' + FloatToStr(summ)
+ #13+ 'Среднее арифметическое: ' + FloatToStr(sr); end;
'/ Функция обеспечивает ввод в ячейку только допустимых символов
procedure TForm1.StringGridlKeyPress(Sender: TObject; var Key: Char);
begin
case Key of
#8,'0'..'9' : ; // цифры и <Backspace>
#13: // клавиша <Enter>
if StringGridl.Col < StringGridl.ColCount - 1
then StringGridl.Col := StringGridl.Col + 1; '.',',':
// разделитель целой и дробной частей числа
begin
if Key <> DecimalSeparator then
Key := DecimalSeparator; // заменим разделитель
// на допустимый
if Pos(StringGridl.Cells[StringGridl.Col,0],
DecimalSeparator) <> 0
then Key := Chr(O); // запрет ввода второго
// разделителя end;
' -' : // минус можно ввести только первым символом,
// т. е. когда ячейка пустая
if Length(StringGrid1.Cells[StringGrid1.Col, 0]) <>0 then
Key := Chr(0) ;
else // остальные символы запрещены
key := Chr(0);
end;
end;
end.
Массивы
Массив — это структура данных, представляющая собой набор переменных одинакового типа, имеющих общее имя. Массивы удобно использовать для хранения однородной по своей природе информации, например, таблиц и списков.
Метод бинарного поиска
На практике довольно часто производится поиск в массиве, элементы которого упорядочены по некоторому критерию (такие массивы называются упорядоченными). Например, массив фамилий, как правило, упорядочен по алфавиту, массив данных о погоде — по датам наблюдений. В случае, если массив упорядочен, то применяют другие, более эффективные по сравнению с методом простого перебора алгоритмы, один из которых — метод бинарного поиска.
Пусть есть упорядоченный по возрастанию массив целых чисел. Нужно определить, содержит ли этот массив некоторое число (образец).
Метод (алгоритм) бинарного поиска реализуется следующим образом:
1. Сначала образец сравнивается со средним (по номеру) элементом массива (рис. 5.10, а).
Если образец больше среднего элемента, то это значит, что искомый элемент расположен ниже среднего элемента (между элементами с номерами sred+1 и niz), и за новое значение verb принимается sred+i, а значение niz не меняется (рис. 5.10, б).
Если образец меньше среднего элемента, то это значит, что искомый элемент расположен выше среднего элемента (между элементами с номерами verh и sred-1), и за новое значение niz принимается sred-1, а значение verh не меняется (рис. 5.10, в).
a
б
в
Рис. 5.10. Выбор среднего элемента массива при бинарном поиске
Рис. 5.11. Алгоритм бинарного поиска в упорядоченном по возрастанию массиве
2. После того как определена часть массива, в которой может находиться искомый элемент, по формуле (niz-verh) /2+verh вычисляется новое значение sred и поиск продолжается.
Алгоритм бинарного поиска, блок-схема которого представлена на рис. 5.11, заканчивает свою работу, если искомый элемент найден или если перед выполнением очередного цикла поиска обнаруживается, что значение verh больше, чем niz.
Вид диалогового окна программы Бинарный поиск в массиве приведен на рис. 5.12. Поле метки Label3 используется для вывода результатов поиска и протокола поиска. Протокол поиска выводится, если установлен флажок выводить протокол. Протокол содержит значения переменных verh, niz, sred. Эта информация, выводимая во время поиска, полезна для понимания сути алгоритма.
Рис. 5.12. Диалоговое окно программы Бинарный поиск в массиве
В форме приложения появился новый компонент, который до этого момента в программах не использовался, — флажок (компонент CheckBox). Значок компонента checkBox находится на вкладке Standard (рис. 5.13). Добавляется к форме он точно так же, как и другие компоненты. Свойства компонента CheckBox перечислены в табл. 5.5.
Таблица 5.5. Свойства компонента CheckBox
Свойство |
Определяет |
||
Name |
Имя компонента. Используется в программе для доступа к свойствам компонента |
||
Caption |
Текст, поясняющий назначение флажка |
||
Checked |
Состояние, внешний вид флажка: если флажок установлен (в квадратике есть "галочка"), то checked = TRUE; если флажок сброшен (нет "галочки"), то Checked=FALSE |
||
State |
Состояние флажка. В отличие от свойства Checked, позволяет различать установленное, сброшенное и промежуточное состояния. Состояние флажка определяют константы: cbChecked (установлен); cbGrayed (серый, неопределенное состояние); cbUnChecked (сброшен) |
||
AllowGrayed |
Может ли флажок быть в промежуточном состоянии: если AllowGrayed = FALSE, то флажок может быть только установленным или сброшенным; если AllowGrayed = TRUE, то допустимо промежуточное состояние |
||
Свойство |
Определяет |
||
Left Top Height Width Font ParentFont |
Расстояние от левой границы флажка до левой границы формы Расстояние от верхней границы флажка до верхней границы формы Высоту поля вывода поясняющего текста Ширину поля вывода поясняющего текста Шрифт, используемый для отображения поясняющего текста Признак наследования характеристик шрифта родительской формы |
||
Рис. 5.13. Компонент CheckBox
После того как компонент CheckBox будет добавлен к форме, а добавляется он обычным образом, нужно установить значения его свойств в соответствии с табл. 5.6.
Таблица 5.6. Значения свойств компонента CheckBox1
Свойство |
Значение |
||
Caption Checked |
Выводить протокол True |
||
В листинге 5.8 приведен текст процедуры обработки события Onclick для командной кнопки Поиск (Button1). Процедура вводит значения элементов массива и образец, затем, используя алгоритм бинарного поиска, проверяет, содержит ли массив элемент, равный образцу. Кроме того, переменная п (число сравнений с образцом) позволяет оценить эффективность алгоритма бинарного поиска по сравнению с поиском методом простого перебора.
При вычислении номера среднего элемента используется функция тгипс, которая округляет до ближайшего целого и преобразует к типу integer выражение, полученное в качестве аргумента. Необходимость использования тгипс объясняется тем, что выражение (niz-verh) /2 — дробного типа, переменная sred — целого, а переменной целого типа присвоить дробное значение нельзя (компилятор выдаст сообщение об ошибке).
Обратите внимание на процедуры обработки события onKeyPress для компонентов stringGridl и Editl. Первая из них обеспечивает перемещение курсора в следующую ячейку таблицы или в поле Editl (из последней ячейки) в результате нажатия клавиши <Enter>, вторая — активизирует командную кнопку Поиск также в результате нажатия клавиши <Enter>.
Листинг 5.8. Бинарный поиск в массиве
unit b_found_;
interface
uses
Windows, Messages, SysUtils, Classes,
Graphics, Controls, Forms, Dialogs, StdCtrls, Grids;
type
TForm1 = class(TForm)
Label1: TLabel;
Label2: TLabel;
Button1: TButton;
Label3: TLabel;
CheckBox1: TCheckBox;
StringGrid1: TStringGrid;
Editl: TEdit;
procedure ButtonlClick(Sender: TObject);
procedure StringGridlKeyPress(Sender: TObject; var Key: Char);
procedure EditlKeyPress(Sender: TObject; var Key: Char); private
{Private declarations }
public
{ Public declarations }
end;
var
Form1: TForm1;
implementation
{$R *.DFM}
{ Бинарным поиск в массиве }
procedure TForm1.Button1Click(Sender: TObject);
const
SIZE=10; var
a:array[1..SIZE] of integer; { массив )
obr:integer; { образец для поиска}
verh:integer; { верхняя граница поиска }
niz: integer; { нижняя граница поиска }
sred:integer; { номер среднего элемента )
found:boolean; { TRUE — совпадение образца с элементом массива }
n:integer; / число сравнений с образцом }
i:integer;
begin
// ввод массива и образца
for i:=l to SIZE do
a[i]:=StrToInt(StringGridl.Cells[i-l,0] ) ;
obr := StrToInt(Editl.text);
// поиск verh:=1;
niz:=SIZE; n:=0;
found:=FALSE; labels.caption:='';
if CheckBoxl.State = cbChecked
then Labels.caption: ='verh'+#9+'niz'#9'sred' #13;
// бинарный поиск в массиве repeat
sred:=Trunc ( (niz-verh) /2)+verh; if CheckBox1.Checked
then Labels.caption:=label3.caption +IntToStr(yerh) + #9
+IntToStr(niz) + #9 +IntToStr(sred) + #13; n:=n+1;
if a[sred] = obr then found:=TRUE else
if obr < a[sred]
then niz:=sred-1 else verh:=sred+1;
until (verh > niz) or found;
if found
then labels.caption:=label3.caption
+'Совпадение с элементом номер '
+ IntToStr(sred)+#13 + 'Сравнений '
+ IntToStr(n)
else label3.caption:=label3.caption
+'Образец в массиве не найден.';
end;
// нажатие клавиши в ячейке StringGrid
procedure TForml.StringGridlKeyPress(Sender: TObject; var Key: Char),
begin
if Key = #13 then // нажата клавиша <Enter>
if StringGrid1.Col < StringGridl.ColCount - 1
then // курсор в следующую ячейку таблицы
StringGridl.Col := StringGrid1.Col +1
else // курсор в поле Editl, в поле ввода образца
Editl.SetFocus;
end;
// нажатие клавиши в поле Editl
procedure TForm1.Edit1KeyPress(Sender: TObject;. var Key: Char);
begin
if Key = #13 // нажата клавиша <Enter>
then // сделать активной командную кнопку
Button1.SetFocus;
end;
end.
Ниже приведены примеры диалоговых окон программы Бинарный поиск в массиве после выполнения поиска— с выводом протокола (рис. 5.14, а) и без протокола (рис. 5.14, б).
Здесь следует обратить внимание на то, что элемент массива, находящийся на седьмом месте, программа бинарного поиска находит всего за четыре шага, в то время как программе, реализующей алгоритм простого перебора, потребовалось бы семь шагов.
а
б
Рис. 5.14. Примеры работы программы бинарного поиска в массиве
Многомерные массивы
В повседневной жизни довольно часто приходится иметь дело с информацией, которая представлена в табличной форме. Например, результат деятельности некоторой фирмы, торгующей автомобилями, может быть представлен в виде табл. 5.7.
Таблица 5.7
|
Январь |
Февраль |
Март |
... |
Ноябрь |
Декабрь |
||
ВA3 2106 |
|
|
|
|
|
|
||
ВA3 2107 |
|
|
|
|
|
|
||
ВA3 2108 |
|
|
|
|
|
|
||
ВA3 2109 |
|
|
|
|
|
|
||
ВАЗ 2110 |
|
|
|
|
|
|
||
ВАЗ 2111 |
|
|
|
|
|
|
||
Колонки и (или) строки таблицы, как правило, состоят из однородной информации. Поэтому в программе, обрабатывающей табличные данные, имеет смысл использовать массивы для хранения и обработки таблиц. Так, приведенная выше таблица может быть представлена как совокупность одномерных массивов:
vaz2106: array [1..12] of integer;
vaz2107: array [1..12] of integer;
vaz2108: array [1..12] of integer;
vaz2109: array [1..12] of integer;
vaz2110: array [1..12] of integer;
vaz2111: array [1..12] of integer;
Каждый из приведенных массивов может хранить информацию о количестве проданных автомобилей одной марки, причем значение элемента массива отражает количество проданных машин в соответствующем месяце.
Возможно и такое представление таблицы:
jan: array [1..6] of integer;
feb: array [1..6] of integer;
mar: array [1..6] of integer;
dec: array [1..6] of integer;
В этом случае каждый массив предназначен для хранения информации о количестве проданных за месяц автомобилей, причем значение элемента массива отражает проданное количество автомобилей одной марки.
Если вся таблица содержит однородную информацию, например, только целые числа, то такая таблица может быть представлена как двумерный массив.
В общем виде инструкция объявления двумерного массива выглядит так:
Имя: array[ НижняяГраница1..ВерхняяГраница1,
НижняяГраница2..ВерхняяГраница2] of Тип
где:
array — слово языка Delphi, указывающее, что объявляемый элемент данных является массивом;
НижняяГраница1, ВерхняяГраница1, НижпяяГраница2, ВерхняяГраница2 — целые константы, определяющие диапазон изменения индексов и, следовательно, число элементов массива;
Тип — тип элементов массива.
Табл. 5.7 может быть представлена в виде двумерного массива следующим образом:
itog: array [1..12, 1..6] of integer
Количество элементов двумерного массива можно вычислить по формуле:
(ВГ1-НГ1+1) х (ВГ2-НГ2+1):
где:
Для того чтобы использовать элемент массива, нужно указать имя массива и индексы элемента. Первый индекс обычно соответствует номеру строки таблицы, второй — номеру колонки. Так, элемент itog [2,3] содержит число проданных в марте (третий месяц) автомобилей марки ВАЗ 2107 (данные о продаже ВАЗ 2107 находятся во второй строке таблицы).
При работе с таблицами (массивами) удобно использовать инструкцию for. Например, фрагмент программы, вычисляющий количество проданных за год автомобилей одного наименования, выглядит так:
s := 0;
for j := 1 to 12 do
s := s + itog[2,j];
Следующий фрагмент программы вычисляет сумму элементов массива (общее количество автомобилей, проданных за год).
s:=0;
for i := 1 to 6 do // шесть моделей автомобилей
for j := 1 to 12 do //12 месяцев s := s + itog[i,j];
В приведенном фрагменте программы каждый раз, когда внутренний цикл (цикл по j) завершается, во внешнем цикле значение i увеличивается на единицу и внутренний цикл выполняется вновь. Таким образом, к текущему значению переменной s последовательно прибавляются значения элементов массива itog: itog[l,l], itog[l,2], ..., itog[l,12], itog[2,l], itog[2,2], ..., itog[2,12] и т. д.
В качестве примера рассмотрим программу, которая обрабатывает результаты спортивных соревнований летней олимпиады в Сиднее, 2000 г. Исходные данные представлены в табл. 5.8.
Таблица 5.8. Результаты олимпиады 2000 г. в Сиднее
Страна |
Золотых |
Серебряных |
Бронзовых |
||
Австралия |
16 |
25 |
17 |
||
Беларусь |
3 |
3 |
11 |
||
Великобритания |
11 |
10 |
7 |
||
Германия |
14 |
17 |
26 |
||
Италия |
13 |
8 |
13 |
||
Китай |
28 |
16 |
15 |
||
Корея |
8 |
9 |
11 |
||
Куба |
11 |
11 |
7 |
||
Нидерланды |
12 |
9 |
4 |
||
Россия |
32 |
28 |
28 |
||
Румыния |
11 |
6 |
9 |
||
США |
39 |
25 |
33 |
||
Франция |
13 |
14 |
11 |
||
Япония |
5 |
8 |
5 |
||
Программа должна вычислить общее количество медалей, завоеванных представителями каждой страны, и соответствующее количество очков (баллов), которое вычисляется по следующему правилу: за каждую золотую медаль команда получает семь очков, за серебряную — шесть очков, за бронзовую — пять очков.
Вид диалогового окна программы приведен на рис. 5.20.
Рис. 5.20. Диалоговое окно программы Итоги олимпиады
Для ввода исходных данных и отображения результата используется компонент StringGrid, свойства которого приведены в табл. 5.9.
Таблица 5.9. Значения свойства компонента StringGrid1
Свойство |
Значение |
||
Name |
Tab1 |
||
ColCount |
6 |
||
RowCount |
14 |
||
FixedCols |
0 |
||
FixedRows |
1 |
||
Options . goEditing |
TRUE |
||
DefaultColWidth |
65 |
||
DefaultRowHeight |
14 |
||
GridLineWidth |
1 |
||
Ячейки первой зафиксированной строки таблицы используются в качестве заголовков колонок таблицы. Во время создания формы приложения нельзя установить значения элементов массива cells, т. к. элементы массива доступны только во время работы программы. Поэтому значения элементов массива Сells, соответствующих первой строке таблицы, устанавливает
процедура обработки события OnActivate (ее текст приведен в листинге 5.11), которое происходит во время активизации формы приложения. Кроме того, эта процедура вписывает в первую колонку таблицы названия стран-участниц соревнований.
Листинг 5.11. Инициализация таблицы
procedure TForml.FormActivate(Sender: TObject); begin
tabl.Cells[0,0] ='Страна';
tabl.Cells[1,0] ='Золотых';
tabl.Cells[2,0] ='Серебряных';
tabl.Cells[3,0] ='Бронзовых';
tabl.Cells[4,0] ='Bcero';
tabl.Cells[5,0] ='Баллов';
tabl.Cells[0,1] ='Австралия';
tabl.Cells[0,2] ='Белоруссия';
tabl.Cells[0,3] ='Великобритания';
tabl.Cells[0,4] ='Германия';
tabl.Cells[0,5] ='Италия';
tabl.Cells[0,6] ='Китай';
tabl.Cells[0,7] ='Корея';
tabl.Cells[0,8] ='Куба';
tabl.Cells[0,9] ='Нидерланды';
tabl.Cells[0,10]— 'Россия';
tabl.Cells[0,ll]:='США';
tabl,Cells[0,12]:='Франция';
tabl.Cells[0,13]:='Япония'; end;
Программа обработки исходной таблицы (листинг 5.12) запускается щелчком мыши на командной кнопке Итоги (Buttoni).
Листинг 5.12. Обработка двумерного массива
procedure TForml.ButtonlClick(Sender: TObject);
var
c,r:integer; // номер колонки и строки таблицы
s:integer; // всего медалей у команды
р:integer; // очков у команды
m:integer; // номер строки с максимальным количеством очков
buf:array[0..5] of string; // буфер для обмена строк
i:integer; // номер строки. Используется во время сортировки
begin
for r:=l to tab1.rowcount do // обработать все строки
begin s:=0;
// вычисляем общее кол-во медалей
for c:=l to 3 do
if tabl.cells[c,r] <>''
then s:=s+StrToInt(tab1.cells[c,r])
else tabl.cells[c,r]:='0'; // вычисляем количество очков
p:=7*StrToInt(tab1.cells[l,r])+
6*StrToInt(tabl.cells[2, r] )
+ 5*StrToInt(tabl.cells[3,r]};
// вывод результата
tabl.cells[4,r]:=IntToStr(s); // всего медалей
tabl.cells[5,r]:=IntToStr(p); // очков
end;
// сортировка таблицы по убыванию в соответствии
// с количеством баллов (по содержимому 5-го столбца)
// сортировка методом выбора
for r:=l to tab1.rowcount-1 do
begin
m:=r; // максимальный элемент — в r-й строке
for i:=r to tabl.rowcount-1 do
if StrToInt(tabl.cells[5,i])>StrToInt(tabl.cells[5,m])
then m:=i;
if r <> m then
begin // обменяем г-ю и m-ю строки таблицы
for c:=0 to 5 do begin
buf[с]:=tab1.Cells[c,r];
tab1.Cells[c,r]:=tabl.Cells[c,m];
tab1.Cells[c,m]:=buf[c];
end;
end;
end;
end;
Сначала для каждой страны программа вычисляет общее количество медалей и соответствующее количество очков. Затем, используя метод простого выбора, программа выполняет сортировку таблицы по убыванию количества набранных очков. Во время сортировки для обмена строк таблицы используется строковый массив buf, индекс которого, как и индекс столбца таблицы, меняется от нуля до пяти (см. инструкцию объявления массива в тексте программы). Такой прием позволяет наиболее просто выполнить копирование замещаемой строки в буфер и замещение строки содержимым буфера.
На рис. 5.21 приведено диалоговое окно программы после завершения процесса обработки массива.
Рис. 5.21. Окно программы Итоги олимпиады
Объявление массива
Массив, как и любая переменная программы, перед использованием должен быть объявлен в разделе объявления переменных. В общем виде инструкция объявления массива выглядит следующим образом:
Имя: array [нижний_индекс. .верхний_индекс] of тип
где:
array — зарезервированное слово языка Delphi, обозначающее, что объявляемое имя является именем массива;
нижний_индекс и верхний_и«декс — целые константы, определяющие диапазон изменения индекса элементов массива и, неявно, количество элементов (размер) массива;
тип — тип элементов массива.
Примеры объявления массивов:
temper:array[1..31] of real;
коef:array[0. .2] of integer;
name:array[1..30] of string[25];
При объявлении массива удобно использовать именованные константы. Именованная константа объявляется в разделе объявления констант, который обычно располагают перед разделом объявления переменных. Начинается раздел объявления констант словом const. В инструкции объявления именованной константы указывают имя константы и ее значение, которое отделяется от имени символом "равно". Например, чтобы объявить именованную константу нв, значение которой равно 10, в раздел const надо записать инструкцию: нв=ю. После объявления именованной константы ее можно использовать в программе как обычную числовую или символьную константу. Ниже в качестве примера приведено объявление массива названий команд-участниц чемпионата по футболу, в котором используются именованные константы.
const
NT = 18; // число команд
SN = 25; // предельная длина названия команды var
team: array[1..NT] of string[SN];
Для того чтобы в программе использовать элемент массива, надо указать имя массива и номер элемента (индекс), заключив индекс в квадратные скобки. В качестве индекса можно использовать константу или выражение целого типа, например:
team [ 1] := 'Зенит';
d := koef[l]*koef[l]-4*koef[2]*koef[1];
ShowMessage(name[n+1]);
temper[i] := StrToFloat(Edit1.text);
Если массив не является локальным, т. е. объявлен не в процедуре обработки события, а в разделе переменных модуля, то одновременно с объявлением массива можно выполнить его инициализацию, т. е. присвоить начальные значения элементам массива. Инструкция объявления массива с одновременной его инициализацией в общем виде выглядит так:
Имя:array [нижний_индекс..верхний_индекс] of тип = (список);
где список — разделенные запятыми значения элементов массива. Например:
a: array[10] of integer = (0,0,0,0,0,0,0,0,0,0);
Team: array[1..5] of String[10]=
('Зенит','Динамо','Спартак','Ротор','СКА');
Обратите внимание, что количество элементов списка инициализации должно соответствовать размерности массива. Если это будет не так, то компилятор выведет сообщения об ошибке: Number of elements differs from declaration (количество элементов не соответствует указанному в объявлении).
При попытке инициализировать локальный массив компилятор выводит сообщение об ошибке: Cannot initialize local variables (локальная переменная не может быть инициализирована). Локальный массив можно инициализировать только во время работы программы, например, так:
for i := 1 to 10 do
a[i]:= 0;
Операции с массивами
Типичными операциями при работе с массивами являются:
ввод массива;
поиск максимального или минимального элемента массива;
поиск заданного элемента массива;
сортировка массива.
Поиск в массиве заданного элемента
При решении многих задач возникает необходимость определить, содержит ли массив определенную информацию или нет. Например, проверить, есть ли в списке студентов фамилия Петров. Задачи такого типа называются поиском в массиве.
Для организации поиска в массиве могут быть использованы различные алгоритмы. Наиболее простой — это алгоритм простого перебора. Поиск осуществляется последовательным сравнением элементов массива с образцом до тех пор, пока не будет найден элемент, равный образцу, или не будут проверены все элементы. Алгоритм простого перебора применяется, если элементы массива не упорядочены.
Сортировка массива
Под сортировкой массива подразумевается процесс перестановки элементов массива, целью которого является размещение элементов массива в определенном порядке. Например, если имеется массив целых чисел а, то после выполнения сортировки по возрастанию должно выполняться условие:
а[1] < а[2] < .. .< a[SIZE]
где SIZE — верхняя граница индекса массива.
Примечание
Задача сортировки распространена в информационных системах и используется как предварительный этап задачи поиска, т. к. поиск в упорядоченном (отсортированном) массиве проводится намного быстрее, чем в неупорядоченном (см. рассмотренный ранее метод бинарного поиска).
Существует много методов (алгоритмов) сортировки массивов.
Рассмотрим два из них:
Сортировка методом обмена
В основе алгоритма лежит обмен соседних элементов массива. Каждый элемент массива, начиная с первого, сравнивается со следующим, и если он больше следующего, то элементы меняются местами. Таким образом, элементы с меньшим значением продвигаются к началу массива (всплывают), а элементы с большим значением — к концу массива (тонут). Поэтому данный метод сортировки обменом иногда называют методом "пузырька". Этот процесс повторяется столько раз, сколько элементов в массиве, минус единица.
На рис. 5.17 цифрой 1 обозначено исходное состояние массива и перестановки на первом проходе, цифрой 2 — состояние после перестановок на первом проходе и перестановки на втором проходе, и т. д.
Рис. 5.17. Процесс сортировки массива
Рис. 5.18. Диалоговое окно программы Сортировка методом обмена
На рис. 5.18 приведено диалоговое окно программы сортировки массива методом обмена.
Процедура сортировки, текст которой приведен в листинге 5.10, запускается нажатием кнопки Сортировка (Button1). Значения элементов массива вводятся из ячеек компонента stringGrid1. Во время сортировки, после выполнения очередного цикла обменов элементов массива, программа выводит массив в поле метки Label2.
Листинг 5.10. Сортировка массива методом обмена
procedure TForm1.Button1Click(Sender: TObject);
const
SIZE=5;
var
a:array[1..SIZE] of integer;
k:integer; // текущий элемент массива
i:integer; // индекс для ввода и вывода массива
changed:boolean; // TRUE, если в текущем цикле были обмены
buf:integer; // буфер для обмена элементами массива
begin
// ввод массива
for i:=1 to SIZE do
a[i] := StrToInt(StringGrid1.Cells[i-1, 0] );
label2.caption:='';
// сортировка массива repeat
Changed:=FALSE; // пусть в текущем цикле нет обменов
for k:=l to SIZE-1 do
if a[k] > a[k+l] then
begin // обменяем k-й и k+1-й элементы
buf := a[k]; a[k] := a[k+l]; a[k+l] := buf;
changed := TRUE;
end;
// вывод массива
for i:=l to SIZE do
Label2.caption:=label2.caption+' '+IntTostr(a[i]);
Label2.caption:=label2.caption+#13;
until not changed; // если не было обменов, значит
// массив отсортирован
Label2.caption:=label2.caption
+#13+'Maccив отсортирован.';
end;
Следует отметить, что максимальное необходимое количество циклов проверки соседних элементов массива равно количеству элементов массива минус один. Вместе с тем возможно, что массив реально будет упорядочен за меньшее число циклов. Например, последовательность чисел 5 1 2 3 4, если ее рассматривать как представление массива, будет упорядочена за один цикл, и выполнение оставшихся трех циклов не будет иметь смысла.
Поэтому в программу введена логическая переменная changed, которой перед выполнением очередного цикла присваивается значение FALSE. Процесс сортировки (цикл repeat) завершается, если после выполнения очередного цикла проверки соседних элементов массива (цикл for) ни один элемент массива не был обменен с соседним, и, следовательно, массив уже упорядочен.
Рис. 5.19. Пример работы программы сортировки массива методом обмена
На рис. 5.19 приведено диалоговое окно программы сортировки массива методом обмена после завершения процесса сортировки.
Сортировка методом прямого выбора
Алгоритм сортировки массива по возрастанию методом прямого выбора может быть представлен так:
1. Просматривая массив от первого элемента, найти минимальный элемент и поместить его на место первого элемента, а первый — на место минимального.
2. Просматривая массив от второго элемента, найти минимальный элемент и поместить его на место второго элемента, а второй — на место минимального.
3. И так далее до предпоследнего элемента.
Ниже представлена программа сортировки массива целых чисел по возрастанию, диалоговое окно которой изображено на рис. 5.15.
Рис. 5.15. Диалоговое окно программы сортировки массива простым выбором
Процедура сортировки, текст которой приведен в листинге 5.9, запускается нажатием кнопки Сортировка (Button1). Значения элементов массива вводятся из ячеек компонента StringGrid1. После выполнения очередного цикла поиска минимального элемента в части массива процедура выводит массив в поле метки (Label2).
Листинг 5.9. Сортировка массива простым выбором
procedure TForm1.ButtonlClick(Sender: TObject);
const
SIZE=10;
var
a:array[1..SIZE] of integer;
min:integer; { номер минимального элемента в части
массива от i до верхней границы массива }
j:integer; { номер элемента, сравниваемого с минимальным }
buf:integer; { буфер, используемый при обмене элементов массива }
i,k:integer;
begin
// ввод массива
for i:=l to SIZE do
a[i]:=StrToInt(StringGridl.Cells[i-1,0]) ; Iabel2.caption:='';
for i:=l to SIZE-1 do begin
{ поиск минимального элемента в части массива от а[1] до a[SIZE]} min:=i;
for j:=i+l to SIZE do if a[j] < a [min]
then min:=j;
{ поменяем местами a [min] и a[i] }
buf:=a[i]; a[i]:=a[min]; a[min]:=buf;
{ вывод массива }
for k:=l to SIZE do
Label2.caption:=label2.caption+' '+IntTostr(a[k]);
Label2.caption:=label2.caption+#13; end;
Label2.caption:=label2.caption+#13+'MaccMB отсортирован.';
end;
На рис. 5.16 приведено диалоговое окно программы после завершения процесса сортировки.
Рис. 5.16. Диалоговое окно программы Сортировка массива
Ввод массива
Под вводом массива понимается процесс получения от пользователя (или из файла) во время работы программы значений элементов массива.
"Лобовое" решение задачи ввода элементов массива — для каждого элемента массива создать поле ввода. Однако если требуется ввести достаточно большой массив, то такое решение неприемлемо. Представьте форму, например, с десятью полями редактирования!
Очевидно, что последовательность чисел удобно вводить в строку таблицы, где каждое число находится в отдельной ячейке. Ниже рассматриваются два варианта организации ввода массива с использованием компонентов
StringGrid И Memo.
Вывод массива
Под выводом массива понимается вывод на экран монитора (в диалоговое окно) значений элементов массива.
Если в программе необходимо вывести значения всех элементов массива, то для этого удобно использовать инструкцию for, при этом переменная-счетчик инструкции for может быть использована в качестве индекса элемента массива.
В качестве примера на рис. 5.1 приведено диалоговое окно приложения, которое демонстрирует инициализацию и процесс вывода значений элементов массива в поле метки. Программа выводит пронумерованный список футбольных команд. Следует обратить внимание, что для того чтобы список команд выглядел действительно как список, свойству Label1.AutoSize нужно присвоить значение False (присвойте свойству Label1.AutoSize значение True и посмотрите, как будет работать программа). Текст программы приведен в листинге 5.1.
Рис. 5.1. Форма и диалоговое окно приложения Вывод массива
Листинг 5.1. Инициализация и вывод массива
unit outar_;
interface
uses
Windows, Messages, SysUtils, Variants,
Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls;
type
TForm1 = class(TForm)
Button1: TButton;
Label1: TLabel;
procedure ButtonlClick(Sender: TObject);
private
{ Private declarations } public
{ Public declarations } end;
var
Form1: TForm1;
implementation
($R *.dfm}
const
NT = 5;
var
team: array[1..NT] of string[10] =
('Зенит','Динамо','Ротор','Спартак','СКА'
procedure TForml.ButtonlClick(Sender: TObject);
var
st:string; // список команд
i:integer; // индекс, номер элемента массива
begin
// формирование списка для отображения в форме
for i:=l to NT do st := st + IntToStr(i)+ ' '
+ team[i] + #13; // вывод списка Label1.Caption := st;
end;
end.