440 likes | 566 Views
Д.з. Задача 3*: последняя ненулевая цифра n!. Вариант 1: (не совсем правильный) p = 1; for (int i = 1; i <= n; i++) { p *= i; … убрать нули на конце … } … переполнение для больших p …
E N D
Задача 3*: последняя ненулевая цифра n! Вариант 1: (не совсем правильный) p = 1; for (int i = 1; i <= n; i++) { p *= i; … убрать нули на конце … } … переполнение для больших p … Вариант 2: (не совсем правильный)Оставлять от p только последнюю цифруНельзя.. 24! = 620448401733239439360000 25! = 15511210043330985984000000 Вариант 3: Оставлять от p несколько последних цифр - работает 6*5 != 4
Задача 3*: схема алгоритма Теперь нас интересует последняя цифра – она точно не нулевая Вариант 4: p = 1; for (int i = 1; i <= n; i++) { … выкинуть из i все сомножители 2 и 5… p *= i; p %= 10; } … умножить на 2 нужное количество раз… 3
int num2 = 0; // Количество двоек int num5 = 0; // Количество пятерок int p = 1; // Последняя цифра произведения for (int i = 2; i <= n; i++) { int i1 = i; while (i1 % 2 == 0) {// Выбрасываем из i все сомножители 2 i1 /= 2; num2++; } while (i1 % 5 == 0) {// Выбрасываем из i все сомножители 5 i1 /= 5; num5++; } p *= i1; p %= 10; } // Добавляем несократившиеся двойки for (int j = 0; j < num2 - num5; j++) { p *= 2; p %= 10; } 4
См также http://oeis.org/A008904 (спасибо за ссылку Дмитрию Гинзбургу)
void reverse(int* a, int n) { int* p = a; int *q = a+n-1; for (; p < q; p++, q--) { int tmp = *p; *p = *q; *q = tmp; } } // Пример вызова int b[100]; … заполнение b … reverse(b, 100); Без указателей void reverse(int* a, int n) { for (int i = 0, j = n-1; i < j; i++, j--) { int tmp = a[i]; a[i] = a[j]; a[j] = tmp; } } Mожно использоватьстандартный swap: swap(*p, *q);swap(a[i], a[j]); Задача 1: reverse
void reverse(int* b, int* e) { for (int *p = b, *q = e; p < q;p++, q--) { swap(*p, *q); } } // Пример вызова int b[100]; … заполнение b … reverse(b, b+99); Задача 1: reverse – другой вариант интeрфейса
Кстати: auto • auto auto переменная = начальное значение; auto x = sin(1); // x будет double void reverse(int* b, int* e) { for (auto p = b, q= e; p < q;p++, q--) { auto tmp = *p; *p = *q; *q = tmp; } } определять тип переменной автоматически, по типу выражения
Задача 2: треугольный массив Это, может быть, и не самый оптимальный вариант, но не мог удержаться:) int* a[10]; // Создаем.. for (int i = 0; i<10; i++) { a[i] = new int[i+1]; } // Заполняем for (int i = 0; i<10; i++) { for (int j = 0; j<=i; j++) { a[i][j] = (i == j); } } // Печатаем for (int i = 0; i<10; i++) { for (int j = 0; j<=i; j++) { cout << a[i][j] << " "; } cout << "\n"; } 9
Задача 3: order Тут удобно воспользоваться перегрузкой имен и назвать эту функцию тоже order. // Вспомогательная функция: // Упорядочиваем два числа void order(int& x, int& y) { if (x > y) { int tmp = x; x = y; y = tmp; } } void order(int& x, int& y, int& z) { order(x, y); order(x, z); order(y, z); } // Пример вызова: int a = 3, b = 1, c = 2; order(a, b, c); cout << a << b << c; 10
Обычно параметры, которыехотят изменить, передают по ссылке void dbl(int& i) { i *= 2; } int m = 10; dbl(m); А можно передать указатель на переменную(так пишут на С) void dbl(int* p) {*p *= 2; } int m = 10; dbl(&m); Задача 4: Передача параметров по указателю(старинный способ изменения параметров:) Теперь m = 10
Как можно (очень примерно) представлять себе, что такое ссылка Ссылка – это (очень примерно) то же, что указатель • который устанавливается один раз при создании • к которому * добавляется автоматически. Ну например: int& x = a[5]; x = 77; cout << x; примерно то же, что int* x = &a[5]; *x = 77; cout << *x;
Что может означать, если параметр функции – указатель? void f(abc *p); // Что может передаваться такой функции? • abc* p = new abc; // Динамическая памятьf(p); • abc a[100]; // Массивf(a); • abc x; // Адрес переменнойf(&x); // (передача параметра по указателю, // вместо использования ссылок. // Обычно встречается в программах на С) чаще всего
typedef 15
typedef Ошибка: у обеих функций одинаковые параметры то же, что int* a[10]; Синтаксис: typedef описание переменной; typedef unsigned long ulong; typedef int* my_array[10]; my_array a; Замечание:typedef не определяет новый тип – это просто сокращенная запись для существующего типа void f(unsigned long i) { … } void f(ulong j) { … } 16
Зачем нужен typedef ? примерно то, что называют инкапсуляция Сокращенная записьtypedef unsigned long ulong; typedef int MY_INTEGER; Если мы планируем потом менять тип Технические причины – определение сложных типов: typedef int* pint; pint a[10];p = new pint[20]; 17
Классы 18
Пример поля список инициализации конструктор методы пример создания объекта пример вызова метода class time { int hour; int min; public: time(int h = 0, int m = 0); void print(); void inc(); }; void time::print() { cout << hour << ":" << min; } time::time(int h, int min) : hour(h), min(m) {} void time::inc() { min++; if ( min == 60 ) { min = 0; hour++; } } time t(9, 30); t.print(); t.inc(); 19
Конструктор Конструктор – особый метод, который всегда вызывается первым, при создании обьекта. • Имя совпадает с именем класса • Может быть много параметров • Довольно часто бывает перегруженили имеет параметры по умолчанию time(int h, int min) : hour(h), min(m) {}
Вызов конструктора • Неявно при описании переменной time t(9, 30); • Неявно при динамическом создании с помощью new time* p = new time(9, 30); • (Относительно редко) Временные объекты f(time(9, 30)); • Если 1 параметр, можно написать так: time t = 11; (то же, что time t(11); ) будет существовать временно, пока выполняется оператор
Списки инициализации • Часто в констукторе пишут так: time::time(int h, int m) {hour = h; min = m;} • Ошибка! Лучше немного иначе: time::time(int h, int m) : hour(h), min(m){} конструктор(…) :поле(нач.значение),поле(нач.значение), …{ …} Почему лучше? • так быстрее • иногда только так и можно • сразу видно, что вы знаете С++ :) список инициализации 22
Деструктор ~имя класса() class abc { … ~abc() { cout << "Пока!"; } }; Задаем какие-то действия при уничтожении объекта 23
public и private • Могут чередоваться • public – можно использовать везде • private – можно использовать только внутри класса Слово: инкапсуляция (information hiding) • Поля обычно private – зачем? • Легче менять • Можно контролировать корректность данных class abc { int i; void f(); public: doublexyz; int g(); private: int fld; int ff(); … }; abc x; x.fld; ошибка - обращение к private полю 24
Еще технические детали • p->abcто же, что (*p).abc Например: p->print(); p->hour; • this – указатель на текущий объект.Mожет использоваться только в методах Например:hour это в методе то же, чтоthis->hour • функции, описанные прямо в классе class abc {void f() { …какой-то код…} • В чем разница structи class? Почти одно и то же. Разница только в том, public или private сначала: • class abc {void f() • struct abc { void f() inline функция private public
Конструктор по умолчанию time t;time* p = new time; Конструктор по умолчанию (default constructor) - конструктор, у которого нет параметров • (или м.б. есть, но для всех задано значение по умолчанию) class time { time(int h = 0, int m = 0); • В частности, вызывается, если создается массив time a[100]; • Если к-ра по умолчанию нет, то будет ошибка какой вызывается конструктор? тоже конструктор по умолчанию
Технические детали: конструкторы по умолчанию создается автоматически Создается автоматически, если вообще нет конструкторов class abc { … нет ни одного конструктора …}; Автоматически добавляется: abc (){} 28
Объявление класса class abc; class klm { abc* p; … }; class abc { klm* q; … }; что-то вроде прототипа, но для классов используем класс до того, как он описан 29
Типичная ошибка class abc { int a, b; } void main() { … Пропущена ; в конце класса! (Сообщение об ошибке, как правило, непонятное). 30
Константы 31
Как определять целые константы? • const int n = 100; Можно использовать в массивах, switch и т.д.int a[n]; 32
enum a=0, b=1, c=2, d=3 a=0, b=1, c=10,d=11,e=12 do do_ ! enum {a, b, c, d}; enum {a,b,c=10,d,e}; можно задавать имя: enum scale {do, re, mi, fa, sol, la, si}; Тогда получится новый тип, его можно использовать при перегрузке и т.д. 33
Константные объекты • const double pi = 3.1415926;const time lunch_time(12, 50);constint a[9] = {7, 9, 11, 13, 15, 19, 22, 28, 48}; Мы обещаем не менять объект Какой смысл? • Компилятор нас проверит (не даст нам менять) • Если мы очень захотим, мы сможем обойти • Иногда компилятор будет запрещать что-то сделать потому что это может привести к изменению const • Компилятор может что-то оптимизировать • Может быть, разместит в особой памяти • Может быть, подставит при компиляции значениеi += a[2]; i += 11; • сonst - полезная информация при чтении программы 34
Константы и указатели Ошибка, *p нельзя менять OK, *p можно менять Ошибка, p нельзя переставлять const int* p; Обещаем не менять место, на которое указывает указатель *p = 5; int* const p; Обещаем не переставлять указатель на другое место *p = 5; p = &i; const int* const p; Обещаем ничего не менять.. 36
Константы и указатели в параметрах функций Ошибка, *p нельзя менять Мы точно знаем, что тут a не изменился. Это удобно… void myfunc(constint* p) { *p = 5; • Обещаем не менять *p внутри функции. Зачем? • Ловим ошибки в программе • Интерфейс функции становится понятнее (легче тем, кто будет использовать) int a[100];myfunc(a); 37
Все тоже относитсяи к ссылкам в параметрах. void f(const abc& x) { … } Обещаем не менять x внутри функции Кстати, зачем вообще const abc& а не abc x? Более эффективно для большихabc Если abc – это базовый класс для иерархии классов(обсудим позже…) Константы и ссылки в параметрах функций 38
Строки ('старое' представление данных, в стиле С) char s[100]; cin >> s; // Ввести до пробела cin.getline(s, 100);// Ввести всю строку Строчки – это просто массивы символов. Конец строки обозначается символом '\0'. Т.е., например, если вы выполнили cin >> s; и ввели строчку "abc", то у вас будет: s[0] == 'a', s[1] == 'b', s[2] == 'c', s[3] == '\0‘; (а в остальных элементах массива s будет неизвестно что). 40
Задачи 1 Описать класс «стек целых чисел». Известно, что в стеке точно не больше 100 чисел. Достаточно определить только конструктор, push и pop.Пример использования:stack s;s.push(3);s.push(5);cout << s.pop(); • То же, что задача 1, но количество элементов не ограничено (т.е. память при необходимости должна отводиться динамически). (Я предложил бы использовать массив, который отводится динамически, а потом при необходимости отводится новый, побольше. Но вы можете реализовать и любой другой способ, их много..) Для тех, кто сделает задачу 2, задачу 1 делать не надо, она засчитается автоматически. Задачи 3 и 4 на следующих слайдах. 42
Задачи 2 Описать для стека метод top() - возвращает последний эл-т как lvalue. Например:s.top() = 5; // Заменить// верхний эл-т стекаs.top()++; // Увеличить// верхний эл-т стека • Описать функцию для сравнения двух строк.Пример вызова: char s1[100]; char s2[100]; cin.getline(s1, 100); cin.getline(s2, 100); bool b = compare(s1, s2); // true, если строки равны • Не забывайте о constв параметрах • Не забывайте, что сравнивать надо только до '\0'. Дальше в строке может идти все что угодно, и это сравнивать не надо. 43
Задачи 3 * (Немного на сообразительность):Определить для стека функцию класса int product(), которая возвращает произведение всех чисел не стеке. При этом эта функция должна работать быстро даже для очень большого стека. Замечания: • Можно придумать реализацию, при которой время вообще не зависит от размера, оно всегда одинаково. • Написать функцию было бы очень просто, если бы на стеке не могло бы быть нулей. • С нулями все немного сложнее, м.б. придется хранить какие-то дополнительные данные. • Обратите внимание, пожалуйста, задачи 3 и 5 не должны правильно работать вместе. Т.е. достаточно, чтобы решение задачи 5 обрабатывало только push и pop, обработка top не обязательна. 44