ВУЗ: Не указан
Категория: Не указан
Дисциплина: Не указана
Добавлен: 30.10.2023
Просмотров: 441
Скачиваний: 1
ВНИМАНИЕ! Если данный файл нарушает Ваши авторские права, то обязательно сообщите нам.
СОДЕРЖАНИЕ
Решение задач с массивами
1 ... 8 9 10 11 12 13 14 15 ... 34
101
while (surveyResponse != -1) {
[ surveyData.push_back(surveyResponse);
cout << "Enter next survey response or -1 to end: ";
cin >> surveyResponse;
}
\ int vectorSize = surveyData.size();
const int MAX_RESPONSE = 10;
int histogram[MAX_RESPONSE];
for (int i = 0; i < MAX_RESPONSE; i++) {
histogram[i] = 0;
}
for (int i = 0; i < vectorSize; i++) {
histogram[surveyData[i] — 1]++;
}
int mostFrequent = 0;
for (int i = 1; i < MAX_RESPONSE; i++) {
if (histogram[i] > histogram[mostFrequent]) mostFrequent = i;
}
mostFrequent++;
В этом коде мы сначала объявляем вектор
X
, а затем резервируем место для 30 ответов на опрос
Y
. Второй шаг не является абсолют- но необходимым, однако резервирование некоторого пространства сверх того, которое требуется для вероятного количества элемен- тов, предотвращает частое изменение размера вектора по мере до- бавления в него новых значений. Мы читаем первый ответ перед циклом ввода данных
Z
, — это метод, который мы впервые исполь- зовали в предыдущей главе и который позволяет проверять каждое введенное значение перед обработкой. В данном случае мы хотим избежать добавления значения-метки –1 к нашему вектору. Результа- ты опроса добавляются в вектор с помощью метода push_back
[
. По- сле завершения цикла ввода данных мы извлекаем размер вектора, используя метод size
\
. Мы могли бы самостоятельно подсчитать количество элементов в цикле ввода данных, но, поскольку вектор уже отслеживает свой размер, это позволит избежать лишних уси- лий. Остальная часть кода аналогична предыдущей версии с масси- вом и фиксированным количеством ответов, за исключением других имен переменных.
Тем не менее в этом обсуждении векторов упущен важный мо- мент. Если мы считываем данные, поступающие непосредственно от пользователя, не зная о том, что мы начинаем с массива или другого набора данных, нам может понадобиться только один массив — для гистограммы. Мы можем не хранить данные опроса в массиве, но обрабатывать их по мере считывания. Нам нужна структура данных только тогда, когда нужно прочитать все значения перед обработкой или нужно обрабатывать значения более одного раза. В этом случае не нужно ни то ни другое: const int MAX_RESPONSE = 10;
int histogram[MAX_RESPONSE];
for (int i = 0; i < MAX_RESPONSE; i++) {
histogram[i] = 0;
}
int surveyResponse;
102 Глава 3
cout << "Enter next survey response or -1 to end: ";
cin >> surveyResponse;
while (surveyResponse != -1) {
histogram[surveyResponse — 1]++;
cout << "Enter next survey response or -1 to end: ";
cin >> surveyResponse;
} int mostFrequent = 0;
for (int i = 1; i < MAX_RESPONSE; i++) {
if (histogram[i] > histogram[mostFrequent]) mostFrequent = i;
}
mostFrequent++;
Несмотря на то что этот код легко было написать, используя пре- дыдущие версии в качестве руководства, было бы еще проще просто прочитать данные пользователя в массив и использовать предыдущий цикл обработки дословно. Преимущество этого процесса «на ходу» за- ключается в эффективности. Мы избегаем необходимости хранить каждый ответ на опрос, когда нужно единовременно сохранять толь- ко один. Наше решение, основанное на применении вектора, было
неэффективным в плане обьема: оно требовало использовать больше ме- ста, чем было необходимо, не давая при этом дополнительных пре- имуществ. Кроме того, прочитывание всех ответов на опрос в вектор само по себе потребовало бы использования цикла в дополнение к ци- клам для обработки всех ответов на опрос и нахождения наибольше- го значения в гистограмме. Это означает, что версия с вектором дела- ет больше, чем вышеприведенная. Поэтому версия с вектором также
неэффективна во времени: она выполняет больше работы, чем требует- ся, не давая при этом дополнительных преимуществ. В некоторых случаях разные решения предлагают компромиссы, а программисты должны выбрать что им важнее: объем или время выполнения. Тем не менее в данном случае использование вектора делает программу не- эффективной во всех отношениях.
В этой книге мы не будем тратить много времени на отслежи- вание любой неэффективности. Программистам иногда приходит- ся заниматься настройкой производительности, которая представляет собой систематический анализ и повышение эффективности про- граммы в плане объема и времени выполнения. Настройка произ- водительности программы во многом напоминает тюнинг гоночно- го автомобиля — это изнуряющая работа, в ходе которой небольшие корректировки могут иметь большие последствия и для выполнения которой необходимы профессиональные знания о том, как механиз- мы работают «за кадром». Тем не менее, даже если у вас нет времени, желания или знаний, чтобы полностью настроить производитель- ность программы, вам все же следует избегать решений, ведущих к серьезным потерям в эффективности. Необязательное использова- ние вектора или массива не похоже на двигатель со слишком бедной смесью топлива и воздуха, скорее это аналогично поездке на пляж на автобусе, когда все ваши вещи могли бы уместиться в небольшом автомобиле.
Решение задач с массивами 103
Если мы уверены в том, что нужно будет обрабатывать данные несколько раз, и мы хорошо представляем себе максимальный раз- мер набора данных, то последним критерием при принятии реше- ния относительно использования массива будет произвольный до- ступ. Позже мы обсудим такие альтернативные структуры данных, как списки, которые, подобно векторам, могут по мере необходимо- сти увеличиваться, но в отличие от векторов и массивов доступ к их элементам может осуществляться только последовательно. То есть, если мы хотим получить доступ к 10-му элементу в списке, мы долж- ны пробежать первые 9 элементов, чтобы добраться до него. На- против, случайный доступ означает, что мы можем получить доступ к любому элементу в массиве или векторе в любое время. Поэтому последнее правило заключается в том, что нам следует использовать массив, когда нужен произвольный доступ. Если нужен только после- довательный доступ, мы можем рассмотреть другую структуру.
Вы можете заметить, что многие из программ в этой главе не учи- тывают последний критерий; мы получаем доступ к данным последо- вательно, а не случайным образом, и все же используем массив. Это приводит нас к значительному исключению для всех этих правил.
Если массив мал, то ни одному из предыдущих возражений не при- дается большой вес. То, что считается «малым массивом» может ва- рьироваться в зависимости от платформы или приложения. Суть в том, что если вашей программе требуется коллекция из 1 или из 10 элементов, каждый из которых требует 10 байт, то вы должны рас- смотреть, стоят ли потенциальные потери 90 байтов, которые могут возникнуть в результате выделения памяти для массива максимально необходимого размера, того, чтобы поискать лучшее решение. Ис- пользуйте массивы мудро, но не позволяйте лучшему быть врагом хо- рошего.
Упражнения
Как всегда, я призываю вас попробовать выполнить как можно боль- ше упражнений.
3.1.
Вы разочарованы тем, что мы мало занимались сортировкой?
Я знаю, как это исправить. Чтобы убедиться в том, что вы осво- ились с функцией qsort, напишите код, в котором эта функция используется для сортировки массива студенческой структуры struct
. Сначала попробуйте сортировать по оценкам, а затем по- пытайтесь сделать это, используя идентификатор студента.
3.2.
Перепишите код для нахождения агента с наивысшим средне- месячным объемом продаж так, чтобы он находил агента с са- мым высоким медианным объемом продаж. Как указывалось ранее, медиана набора значений представляет собой такое зна- чение, находящееся в середине, что половина значений набора больше него, а другая половина меньше. При наличии четного количества значений медиана является простым средним двух
значений в середине. Например, в наборе 10, 6, 2, 14, 7, 9 значе- ния, находящиеся в середине, — это 7 и 9. Среднее значение 7 и
9 равно 8, поэтому 8 — это медиана.
3.3.
Напишите функцию bool, которой передается массив и коли- чество элементов в этом массиве и которая определяет, будут ли данные в этом массиве сортироваться. Для этого требуется толь- ко одна передача!
3.4.
Вот вариация задания с массивом значений const. Напишите программу для шифрования методом подстановки. При таком шифровании все сообщения состоят из прописных букв и зна- ков препинания. Исходное сообщение называется открытым текстом, зашифрованный текст создается путем замены каждой буквы другой буквой (например, каждая буква C может стать бук- вой X). Для этой задачи жестко закодируйте массив const из 26 элементов char для шифрования и сделайте так, чтобы ваша про- грамма прочитывала открытый текст и выводила эквивалент со- общения в виде зашифрованного текста.
3.5.
Сделайте так, чтобы предыдущая программа преобразовывала зашифрованный текст обратно в открытый текст для проверки корректности кодирования и декодирования.
3.6.
Для усложнения задачи шифрования текста сделайте так, чтобы ваша программа случайным образом генерировала шифроваль- ный массив вместо жестко закодированного массива const. Фак- тически это означает помещение случайного символа в каждый элемент массива, однако помните, что вы не можете заменить букву самой собой. Итак, первым элементом не может быть бук- ва A, и вы не можете использовать ту же букву для двух подстано- вок, то есть, если первым элементом является буква S, ни один другой элемент не может быть буквой S.
3.7.
Напишите программу, которой дан массив целых чисел и кото- рая определяет моду, то есть наиболее часто встречающееся в массиве число.
3.8.
Напишите программу, которая обрабатывает массив объектов student и определяет квартили оценок, то есть оценки, которые необходимо получить студенту, чтобы успевать также хорошо или лучше, чем 25% студентов, 50% студентов и 75% студентов.
3.9.
Рассмотрите следующую модификацию массива sales: поскольку продавцы приходят и уходят в течение года, теперь мы отмеча- ем месяц, предшествующий найму торгового агента или следую- щий за последним месяцем его работы, значением –1. Перепи- шите код для нахождения самого высокого значения среднего объема продаж или наибольшего значения медианного объема продаж, чтобы это компенсировать.
9 равно 8, поэтому 8 — это медиана.
3.3.
Напишите функцию bool, которой передается массив и коли- чество элементов в этом массиве и которая определяет, будут ли данные в этом массиве сортироваться. Для этого требуется толь- ко одна передача!
3.4.
Вот вариация задания с массивом значений const. Напишите программу для шифрования методом подстановки. При таком шифровании все сообщения состоят из прописных букв и зна- ков препинания. Исходное сообщение называется открытым текстом, зашифрованный текст создается путем замены каждой буквы другой буквой (например, каждая буква C может стать бук- вой X). Для этой задачи жестко закодируйте массив const из 26 элементов char для шифрования и сделайте так, чтобы ваша про- грамма прочитывала открытый текст и выводила эквивалент со- общения в виде зашифрованного текста.
3.5.
Сделайте так, чтобы предыдущая программа преобразовывала зашифрованный текст обратно в открытый текст для проверки корректности кодирования и декодирования.
3.6.
Для усложнения задачи шифрования текста сделайте так, чтобы ваша программа случайным образом генерировала шифроваль- ный массив вместо жестко закодированного массива const. Фак- тически это означает помещение случайного символа в каждый элемент массива, однако помните, что вы не можете заменить букву самой собой. Итак, первым элементом не может быть бук- ва A, и вы не можете использовать ту же букву для двух подстано- вок, то есть, если первым элементом является буква S, ни один другой элемент не может быть буквой S.
3.7.
Напишите программу, которой дан массив целых чисел и кото- рая определяет моду, то есть наиболее часто встречающееся в массиве число.
3.8.
Напишите программу, которая обрабатывает массив объектов student и определяет квартили оценок, то есть оценки, которые необходимо получить студенту, чтобы успевать также хорошо или лучше, чем 25% студентов, 50% студентов и 75% студентов.
3.9.
Рассмотрите следующую модификацию массива sales: поскольку продавцы приходят и уходят в течение года, теперь мы отмеча- ем месяц, предшествующий найму торгового агента или следую- щий за последним месяцем его работы, значением –1. Перепи- шите код для нахождения самого высокого значения среднего объема продаж или наибольшего значения медианного объема продаж, чтобы это компенсировать.
105
В этой главе мы научимся решать задачи с использованием указа- телей и динамической памяти, ко- торые позволят нам писать гибкие программы, способные обрабатывать заранее не- известные объемы данных. Использование указа- телей и динамическое управление памятью – это высший пилотаж в программировании.
Если вы умее- те писать программы, способные выделять память в процессе выпол- нения, объединять их в полезные структуры и высвобождать по завер- шении, то вы не просто «кодер», вы — самый настоящий программист.
Из-за сложности применения указателей и из-за того, что мно- гие популярные языки, например Java отказываются от их исполь- зования, некоторые начинающие программисты могут решить
+ "
" $ " =
4
106 Глава 4
пропустить изучение данного вопроса. Но это было бы ошибкой с их стороны. Серьезное программирование всегда будет использо- вать указатели и косвенную адресацию, даже если они будут скрыты за конструкциями языка высокого уровня. Следовательно, чтобы по- настоящему мыслить как программист, вы должны уверенно приме- нять указатели и решать связанные с ними задачи.
Однако перед тем как перейти к решению задач с использовани- ем указателей, мы тщательно изучим все аспекты их функциониро- вания, как лежащие на поверхности, так и подводные камни. Это ис- следование принесет нам двойную пользу. Во-первых, мы научимся применять указатели наиболее эффективным способом. Во-вторых, развеяв завесу тайны вокруг указателей, мы сможем избавиться от страха перед их использованием.
Обзор основных свойств указателей
Как и в отношении тем, рассматриваемых в предыдущих главах, вы уже должны знать, что такое указатели, но для большей ясности про- бежимся по их основным свойствам.
В C++ указатели обозначаются с помощью символа звездочки (*).
Причем, в зависимости от контекста звездочка может означать как объявление указателя , так и непосредственное обращение к памяти.
Чтобы объявить указатель, мы ставим звездочку между типом дан- ных и идентификатором переменной:
int * intPointer;
В примере объявлена переменная intPointer, которая являет- ся указателем на данные типа int. Обратите внимание, что звездоч- ка относится к идентификатору, а не к типу. В следующем примере variable1
— это указатель на int, а variable2 обычная переменная типа int:
int * variable1, variable2;
Оператор & перед именем переменной возвращает адрес ячейки.
Это позволяет нам записать адрес переменной variable2
в указатель variable1
следующим образом:
variable1 = &variable2;
Мы также можем напрямую присвоить значение одного указате- ля другому:
intPointer = variable1;
Но особенно важно то, что прямо во время выполнения про- граммы мы можем выделять память, доступ к которой можно осуще- ствить только с помощью указателя. Для этой цели обычно исполь- зуют оператор new :