Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

Lab03_2010

.pdf
Скачиваний:
29
Добавлен:
07.06.2015
Размер:
1.72 Mб
Скачать

Мы воспользуемся классом ArrayList, объект которого представляет собой «одномерный динамический массив»: структуру данных с доступом к элементу по индексу и с возможностью изменения количества элементов по ходу выполнения программы. Класс ArrayList, как и большинство классов-коллекций, является настраиваемым: программист может определить, какого типа элементы будут в нем содержаться. Тип элемента может быть только классовым, поместить в коллекцию объект «примитивного» типа нельзя (и это одна из причин, по которой в Java существуют так называемые «классы-оболочки» Integer, Double и другие).

Опишем класс для хранения номеров строк и столбцов. Поскольку вне класса Matrix такой класс не будет иметь большого смысла, сделаем его внутренним (рис. 62). Поле row будет хранить номер строки, поле column – номер столбца. Конструктор класса будет получать два параметра, позволяющие инициализировать поля создаваемого объекта.

Рис. 62. Внутренний класс Coordinates

Теперь опишем метод, который будет возвращать как раз объект класса ArrayList, содержащий объекты класса Coordinates. Когда Вы напечатаете ArrayList, среда предложит импортировать этот класс из пакета java.util (рис. 63).

Рис. 63. Класс ArrayList нужно импортировать из java.util

31

Заголовок метода должен выглядеть, как на рис. 64. Среда сообщает об отсутствии предложения return, обязательного для методов, имеющих тип возвращаемого значения, отличный от void. Разумеется, такой оператор появится в нашем методе, когда он будет полностью написан.

Рис. 64. Заголовок метода getNearestPoints()

Пока же обратите внимание на новый синтаксис: ArrayList<Coordinates>. В угловых скобках указан класс, объекты которого будут помещаться в объект класса ArrayList. Для того, чтобы работать с этой структурой, понадобится сначала создать именно объект класса ArrayList (рис. 65), а затем (по мере создания) помещать в него объекты класса Coordinates. При создании этого объекта также необходимо указывать в угловых скобках, в качестве хранилища объектов какого класса он создается.

Рис. 65. Создание объекта ArrayList

Когда очередная пара координат будет получена при анализе элемента двумерного массива, будет сформирован объект класса Coordinates, который затем будет добавляться в объект result.

§ 8. Отыскание элементов, равных заданному, с некоторой точностью

Чтобы получить координаты элементов, отличающихся от заданного значения не более, чем на некоторую величину, организуем просмотр этих элементов с помощью двух вложенных циклов – по строкам (внешний) и по столбцам (внутренний). Сравнение элемента массива array[i][j] с заданным числом desiredNumber выполняется по модулю (для чего используется функция Math.abs()). Добавление новой пары координат к объекту result осуществляется с помощью метода add(Coordinates e) (рис. 66).

32

Рис. 66. Метод add(Coordinates e) добавит новую пару координат в конец уже существующего списка пар координат

Вводить объект – переменную класса Coordinates – нет необходимости. Конструктор объекта можно вызвать сразу при добавлении элемента (рис. 67):

Рис. 67. Создание нового объекта Coordinates происходит непосредственно при добавлении его к result

Метод printNearestPoints() будет выводить в файл c.txt пары найденных координат элементов – по одной паре в строке, сначала номер строки, потом номер столбца через пробел. В классе ArrayList определен метод size(), возвращающий количество элементов, содержащихся в объекте этого класса, а также метод get(int index), который по целому

33

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

Рис. 68. Метод printNearestPoints()

Поясним отдельно строчку 143. Метод println() объекта fout фактически принимает в качестве параметра строку, которая получается конкатенацией пробела и двух целых чисел (которые при сложении со строкой автоматически преобразуются в строковое представление). Первое целое число – это номер строки, сохраненный в объекте класса Coordinates, помещенного в список таких объектов, создаваемый методом getNearestPoints(), k-ым по счету (при счете с нуля), второе же – номер столбца. Их «извлечение» происходит следующим образом: из объекта al по номеру k с помощью метода get получается объект класса Coordinates. Когда такой объект получен, через квалификатор-точку мы можем обращаться к его полям и методам. В нашем случае происходит обращение к полям row и column.

Добавьте вызов этого метода в метод main() главного класса (рис. 69) и запустите приложение на выполнение.

Рис. 69. Вызов метода printNearestPoints() в методе main()

Найдите во вкладке Files (как на рис. 48) файл c.txt и откройте его содержимое в главном окне (рис. 70). Проверьте (отыскивая соответствующие элементы в исходном массиве, записанном в input.txt), что на пересечении указанных строк и столбцов действительно стоят числа, удовлетворяющие заданным условиям. Убедитесь, что никаких других чисел, удовлетворяющих заданному условию, в исходном массиве нет.

34

Рис. 70. Полученный файл с координатами подходящих чисел

Измените точность, с которой следует сравнивать числа, в файле input.txt с 0.1 на 0.05. Сохраните файл input.txt и вновь запустите приложение на выполнение. Посмотрите, сколько и каких пар координат будет содержаться в обновленном файле c.txt. Попробуйте менять в исходном файле сравниваемое число и точность сравнения, проследите за получаемыми результатами. В частности, в качестве искомого числа введите 15.2, а в качестве точности 0.1. Когда Вы сохраните input.txt и вновь запустите приложение на выполнение, файл c.txt окажется пустым. Это правильно: в заданном массиве действительно нет подходящих чисел. Однако по условию задачи следует сообщить об отсутствии таких элементов (отсутствие данных в выходном файле можно считать таким сообщением с очень большой натяжкой). Поэтому внесем небольшое дополнение в метод printNearestPoints() (рис. 71).

Рис. 71. Перед выводом выполним проверку размера a1

Перед тем, как выводить что-либо в файл, в операторе if производится проверка размера a1. Если ни одного элемента в a1 при просмотре массива добавлено не было, то размер его останется нулевым, и можно вывести сообщение об отсутствии таких элементов. В

35

противном же случае выполняется код, который был написан ранее. Изменения придется вносить вручную, такого шаблона изменений «по умолчанию» среда не предоставляет.

Если теперь Вы запустите приложение на выполнение, оставив в input.txt в качестве последней строки 15.2 и 0.1, то получите файл c.txt с соответствующим сообщением (рис. 72):

Рис. 72. Сообщение об отсутствии искомого числа в массиве

Теперь задание в) можно считать выполненным.

Усовершенствуйте класс Coordinates, дополнив его методом toString(), который будет формировать строчку – строковое представление объекта этого класса. В этом случае после получения объекта класса Coordinates с помощью метода get() потребуется вызвать именно метод toString(), чтобы вывести в файл необходимые данные. Проверьте работоспособность приложения.

§ 9. Создание копии массива для замены элементов

Приступим к решению последней части задачи. В ней требуется не только найти удовлетворяющие условию элементы, но и заменить их. Когда в задаче требуется замена исходных данных, нужно проанализировать: можно ли выполнить замену без создания копии исходных данных или нет. В пункте г) элемент сопоставляется со своими соседями по строке и соседями по столбцу (с обеих сторон), и на основании результатов сопоставления делается (или не делается) замена его суммой всех его соседей. Однако при этом заменяемый элемент сам является соседом (по строке или по столбцу) как минимум для двух других элементов. Организовать просмотр элементов таким образом, чтобы измененный элемент уже не участвовал в сравнениях, невозможно. Поэтому для решения задачи будем создавать копию массива.

Примечание.

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

Это практически равноценно созданию копии массива, только в «построчном режиме».

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

36

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

Какими значениями должны быть заполнены добавленные строки и столбцы? На этот вопрос можно ответить, обратившись к условию задачи. Элемент может быть заменен, если все его соседи по столбцу или по строке либо больше него, либо меньше. Элементы из добавленных строк и столбцов не должны изменять истинность утверждения для «оригинальных» элементов. Так, верхняя добавленная строка содержит соседей сверху для элементов нулевой строки исходного массива. Если эти соседи будут в точности совпадать с соседями снизу, то утверждение нарушено не будет. Нижняя добавленная строка содержит соседей снизу для элементов последней строки исходного массива, и потому они могут быть копиями реальных соседей сверху. Аналогичные рассуждения можно провести и для столбцов.

Поскольку создание копии вполне самостоятельная задача, можно оформить ее в виде отдельного метода (рис. 73).

Рис. 73. Метод getBoundMatrix()

Вложенные циклы по i и j обеспечивают копирование исходного массива в массив копию со смещением на один элемент вправо и вниз. Цикл по t заполняет добавленные столбцы, а цикл по s – добавленные строки. Угловые элементы нового массива не

37

получают никаких значений (а, следовательно, остаются равными нулю – при создании элементы любого числового массива инициализируются нулевыми значениями). Впрочем, они не используются, поскольку не являются соседями ни для одного из элементов исходного массива.

Примечание.

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

§ 10. Организация замены элементов

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

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

Примечание.

Конечно, можно было бы проверять, «настоящий» элемент включается в сумму или фиктивный. Но при описанном подходе мы еще раз используем объект класса ArrayList и применим технику обхода соседей элемента с помощью массивов сдвигов координат.

Условия, которые должны быть выполнены, чтобы элемент был заменен, выглядят достаточно громоздко, поэтому оформим их в виде отдельных методов проверки. Договоримся, что если элемент оказался больше обоих соседей (по столбцу или по строке), то метод проверки возвратит +1, если меньше обоих – то –1, в остальных случаях

– ноль. Тогда, чтобы выяснить, что элемент должен быть заменен, нам потребуется убедиться, что одна из проверок дала нам +1, а другая –1.

Рис. 74. Сравнение элемента с соседями по строке

38

Методы сравнения элемента с соседями по строке (рис. 74) и с соседями по столбцу (рис. 75) похожи. Оба метода возвращают целое число (согласно приведенному выше описанию), принимают три параметра – собственно сам массив-копию, (целый) номер столбца и (целый) номер строки – местоположение анализируемого элемента.

Рис. 75. Сравнение элемента с соседями по столбцу

Получение списка элементов, которые следует заменить, идейно ничем не отличается от получения списка элементов в пункте в) задачи (рис. 76).

Рис. 76. Получение списка элементов, подлежащих замене

Чтобы установить, что одна из проверок дает +1, а другая –1, мы просто перемножили их результаты. Ответ –1 может получиться только в этом случае (убедитесь в этом). В качестве единственного параметра метод принимает массив-копию, а возвращает список пар координат элементов, подлежащих замене.

Следующим шагом будет обнуление дополнительных строк и столбцов массивакопии (рис. 77). Отметим, что два цикла, составляющие тело этого метода, практически идентичны соответствующим циклам в методе, создающем массив-копию (рис. 73; переменные циклов в них намеренно использованы те же самые). Однако описать один метод, который бы работал в обоих случаях, просто используя разные параметры не получится (обоснуйте это утверждение).

39

Рис. 77. Обнуление границ в массиве-копии

Метод getSumNeighborhood() написан для подсчета суммы соседей элемента (рис. 78). Чтобы перечислить всех соседей элемента, воспользуемся массивами сдвигов shiftRows и shiftColumns. Порядок элементов массивов сдвигов позволяет организовать перечисление соседей элемента, начиная с верхнего соседа по столбцу по часовой стрелке (проверьте это, выполнив пошаговую трассировку метода для некоторых заданных r и c вручную).

Рис. 78. Подсчет суммы соседей элемента

Теперь, наконец, можно собрать все вспомогательные методы воедино. Это сделано в методе changeMatrix() (рис. 79). Сначала строится массив-копия с дополнительными строками и столбцами. Затем получается список элементов массива, подлежащих замене. На следующем шаге элементы дополнительных строк и столбцов массива-копии обнуляются, чтобы при суммировании соседей элементов получался правильный результат. После этого просматривается список элементов, подлежащих замене, вычисляется сумма их соседей, и результат записывается в исходный массив array на место этого элемента. Разумеется, учитывается то, что в массиве-копии номера строк и

40

Соседние файлы в предмете Программирование на Java