Результаты прохождения курса "28 задач повышающейся сложности"
Что ж, вот и пришла пора написать свой первый серьёзный пост здесь. Изначально я завёл блог в VK, но там не хватало возможности форматировать текст и было решено переехать сюда. Гитхаб позволяет размещать у себя статические веб-страницы бесплатно, а чтобы с лёгкостью вести простой блог придуман Jekyll, который переводит контент из markdown в статические веб-страницы. Это позволяет один раз настроить шаблоны, которые будут применяться в нашем блоге и думать только о написании текстов.
ВКонтакте я успел описать решение половины задач курса, сюда перенес лишь моменты с различными открытиями и интересностями. Тот блог я планирую свернуть, целиком сосредоточившись на этом, потому ссылку на него не привожу. Код всех учебных задач, текущих и будущих, можно найти тут.
Курс “28 задач повышающейся сложности” Высшей школы программирования Сергея Бобровского я проходил на языке Java, с некоторым опытом программирования на C#. При решении первых задач больше времени уходило на разбирательства с синтаксисом, то есть Java для меня был чем-то новым.
Перейдём к моим наблюдениям и открытиям.
Тип данных char
Оказывается, что переменной символьного типа можно присвоить не только конкретный символ (Например char c = '0'
), но и целочисленное значение (char c = 48
, что равносильно предыдущему примеру) от 0 до 65535. В этом случае она опять же будет хранить символ, а целочисленное значение будет указывать на номер символа в таблице символов Unicode (UTF-16).
При вычитании или сложении символов операции проводятся над их кодами (Пример: ‘5’ - ‘1’ = 4 или ‘5’ + ‘1’ = 102; код символа ‘1’ - 49, символа ‘5’ - 53; Поэтому мы и имеем такие результаты). Цифры в таблице кодировки идут по порядку от 0 до 9 и имеют коды от 48 до 57, поэтому мы и имеем такие результаты в примере.
При вычитании значений char получим код типа int, чтобы у нас в итоге был символ можно использовать преобразование типов: добавить (char)
перед результатом вычитания.
Теперь немного о буквах.
Решил посмотреть коды различных символов нашего алфавита и увидел, что ‘я’ имеет код 1103, а ‘ё’- 1105. Стоит учитывать этот момент, если вы захотите сортировать строки, содержащие ‘ё’, в лексикографическом порядке.
Прерывание цепочки вложенных циклов
Есть цикл в цикле, мы дошли до какого-то значения когда нужно закончить их работу, но впереди ещё множество итераций, а простой break
выводит только из внутреннего цикла. Здесь поможет break
с меткой. Пишем название метки перед блоком кода, который хотим прервать, после чего используем оператор break c именем нашей метки. Результат - работа циклов прервана.
...
label: //Метка перед блоком кода, который нужно будет прервать
for (int i = 0; i < 4; i++) {
System.out.print("Проход " + i + ": ");
for (int j = 0; j < 50; j++) {
if (j == 10) {
break label; //Выйти из циклов
}
System.out.print(j + " ");
}
...//После прерывания исполняем дальнейший код
Проверка на выход за границы двумерного массива
В задачах 3 и 23 проходила работа с двумерными массивами, для их решения нужно было изменять значения определённых элементов и их 4 соседей, по вертикали и горизонтали. Самое простое - сделать цикл, который будет брать и менять элемент с соседями. Но что если он находится на краю массива, а в цикле стоит изменение всех соседних элементов. При решении третьей задачи я, только начавший постигать Java, использовал много конструкций if
. Даже приведу код, чтобы вы сами увидели насколько это громоздко.
if (field[i][j] == 1 && i == 0) { //Захват клеток, у верхней границы
if (j == 0) {
if (field[i+1][j] == 0) {field[i+1][j] = 2;}
if (field[i][j+1] == 0) {field[i][j+1] = 2;}
} else if (j == field[i].length - 1) {
if (field[i+1][j] == 0) {field[i+1][j] = 2;}
if (field[i][j-1] == 0) {field[i][j-1] = 2;}
} else {
if (field[i+1][j] == 0) {field[i+1][j] = 2;}
if (field[i][j-1] == 0) {field[i][j-1] = 2;}
if (field[i][j+1] == 0) {field[i][j+1] = 2;}
}
}
if (field[i][j] == 1 && i == field.length - 1) { //Захват клеток, у нижней границы
if (j == 0) {
if (field[i-1][j] == 0) {field[i-1][j] = 2;}
if (field[i][j+1] == 0) {field[i][j+1] = 2;}
} else if (j == field[i].length - 1) {
if (field[i-1][j] == 0) {field[i-1][j] = 2;}
if (field[i][j-1] == 0) {field[i][j-1] = 2;}
} else {
if (field[i-1][j] == 0) {field[i-1][j] = 2;}
if (field[i][j-1] == 0) {field[i][j-1] = 2;}
if (field[i][j+1] == 0) {field[i][j+1] = 2;}
}
}
if (field[i][j] == 1 && i !=0 && i != field.length - 1) { //Захват средних клеток
if (j == 0) {
if (field[i-1][j] == 0) {field[i-1][j] = 2;}
if (field[i+1][j] == 0) {field[i+1][j] = 2;}
if (field[i][j+1] == 0) {field[i][j+1] = 2;}
} else if (j == field[i].length - 1) {
if (field[i-1][j] == 0) {field[i-1][j] = 2;}
if (field[i+1][j] == 0) {field[i+1][j] = 2;}
if (field[i][j-1] == 0) {field[i][j-1] = 2;}
} else {
if (field[i-1][j] == 0) {field[i-1][j] = 2;}
if (field[i+1][j] == 0) {field[i+1][j] = 2;}
if (field[i][j-1] == 0) {field[i][j-1] = 2;}
if (field[i][j+1] == 0) {field[i][j+1] = 2;}
}
}
Эта махина позволяла менять только те элементы, что находятся в массиве. К 23 задаче я изучил язык получше и смог применить другое решение.
int[] neighbors = new int[] {i,i,i+1,i-1,j,j,j+1,j-1}; //Массив индексов соседей
for (int c = 0; c < 4; c++) { //Изменение соседних элементов
try {
if (tree2D[neighbors[c]][neighbors[7-c]] < '3'){ //Выполняем условия задачи
tree2D[neighbors[c]][neighbors[7-c]] = '0'; //Выполняем условия задачи
}
} catch (ArrayIndexOutOfBoundsException e){
continue; //Ловим исключение и пробуем изменить оставшихся соседей
}
}
Как видно, его суть кроется в использовании исключений. Я сделал массив из 8 чисел, для обозначения индексов четырёх соседних элементов, числа расположены так, что если брать крайние элементы и двигаться к середине, то мы пройдёмся по всем соседям. Если элемент находиться за границей массива, то мы просто ловим выброшенное исключение, которое, в ином случае, прекратило бы работу программы и пробуем изменить другой соседний элемент.
И это всё?
Всё, из написанного в блоге VK, чем бы я хотел поделиться тут. От самого курса я получил гораздо больше, здесь лишь та информация, которую не просто найти в интернете. Многие вещи, которые я использовал при решении задач были найдены на популярнейших сайтах, находящихся в топе поисковой выдачи и дублировать её здесь нет смысла(не хочется первым постом портить отношения с роботами поисковиков:). Мой уровень знания языка заметно подрос, благодаря этому курсу, а что ещё важнее – прокачалось мышление. Также я смог увидеть некоторые свои типичные ошибки, над которыми нужно поработать, чтобы стать лучше.
Заключение
Вот и подошёл к концу первый серьёзный пост в этом блоге. Здесь были показаны некоторые наблюдения и открытия, впечатлившие меня при прохождении курса.
Я уже начал привыкать к markdown, вспомнил как проходил курс, порадовался за свой рост, ибо несколько раз хотелось всё бросить, потому что было сложно, но я справился и теперь собираюсь двигаться дальше, к более сложным задачам и новым уровням знаний.
А на этом всё, спасибо за внимание и до следующей встречи на страницах моего блога.