
- •Ответы по дисциплине «Основы алгоритмизации и программирования»
- •Запись алгоритма Евклида на языке с
- •Int main() {
- •Эвристический алгоритм «ближайшего соседа»
- •Эвристический алгоритм «ближайших пар»
- •«Правильный» алгоритм поиска маршрута
- •Эволюция языка с bcpl → b → c → k&r c → ansi c → c99 → c1x
- •#Define имя текст_для_подстановки
- •123, 67543, 037, 07777, 0Xabf7, 0xffff, …
- •123456789L, 0xful (это просто число 15).
- •Определение символических констант в limits.H
- •Int lower, upper, step;
- •Int main() {
- •Int main() {
- •Int main() {
- •Всего операций: 47
- •If (условие) оператор
- •If (условие) оператор1 else оператор2
- •Int main() {
- •Int main() {
- •Int main() {
- •Int main() {
- •If (found)
- •Адресация памяти
- •Адреса объектов программы
- •Int fact(int n) {
- •О размерах участков памяти, выделяемых объектам
- •Правила адресной арифметики
- •Никакие другие операции к адресам неприменимы, т.Е. Адреса нельзя умножать, делить, складывать между собой и пр.
- •Имя массива – это константный указатель на его начало.
- •T X[] эквивалентно t *X
- •Int main() {
- •Void *calloc(size_t n, size_t r)
- •Void free(void *p)
- •Int main() {
- •Void *p;
- •Void swaps(char** a, char** b) {
- •Int main(void) {
- •Int main() {
- •Правило «право-лево»
- •Int pt_in_rect(struct point p, struct rect r) {
- •Int main() {
- •Int main() {
- •Int ival;
- •Void init(Vector*);
- •Void resize(Vector*, int);
- •Void push_back(Vector*, double);
- •Void push_s(Stack *st, double d) {
- •Void init_q(Queue *q) {
- •Void enqueue(Queue *q, double d) {
- •Int dequeue(Queue *q, double *d) {
- •Typedef struct Heap {Vector V;} Heap;
- •Void init_h(Heap *hp) {
- •Int Heap_Maximum(Heap *hp, double *z) {
- •Void Max_Heap_Insert(Heap *hp, double X){
- •Void Max_Heapify(Heap *hp, int I) {
- •Int l, r, largest;
- •Int Heap_Extract_Max(Heap *hp, double *z) {
- •Void Build_Max_Heap(Heap *hp) {
- •Void Insert_head_l1(List1 *l, double z) {
- •Void Insert_back_l1(List1 *l, double z) {
- •Int Extract_head_l1(List1 *l, double *z) {
- •Int Extract_back_l1(List1 *l, double *z) {
- •Void reverse_l1(List1 *l) {
- •Исходный код функции sort_l1
- •Void sort_l1(List1 *l) {
- •Void visit(List1* l) {
- •Void traverse(List1* l) {
- •Void Print_l1(List1 *l) {
- •Void Insert_l2(List2 *l, double z, int direction) {
- •Прямой обход (сверху вниз), при котором мы посещаем узел, а затем левое и правое поддеревья
- •Поперечный обход (слева направо), при котором мы посещаем левое поддерево, затем узел, а затем правое поддерево
- •Обратный обход (снизу вверх), при котором мы посещаем левое и правое поддеревья, а затем узел.
- •Простой метод сортировки массива
- •Задача о взвешивании монет
- •1) Очевидно, что на последнем шаге процедуры взвешивания мы должны иметь дело максимум с 3 монетами, чтобы в при любом исходе взвешивания получить результат.
- •2) Задача предпоследнего шага – отобрать группу из 3-х монет. Это можно сделать, если в нашем распоряжении будет не более 9 монет (3 группы по 3 монеты).
- •3) Наконец, если у нас будет от 10 до 27 монет, мы сможем отобрать из них не более 9
- •Void mov(int n, char a, char c, char b) {
- •Int main() {
Int Heap_Extract_Max(Heap *hp, double *z) {
if(hp->v.sz == 0) return 0; // если пирамида пуста - выход
*z = hp->v.elem[0];
hp->v.elem[0] = hp->v.elem[--(hp->v.sz)];
Max_Heapify(hp, 0); // спуск элемента, находящегося в корне
return 1;
}
Вопрос №80. Алгоритм преобразования массива в пирамиду.
Решим следующую задачу: заданный массив преобразовать в пирамиду. Пусть задан массив чисел: {1,2,3,4,7,8,9,10,14,16}. Изобразим его в форме пирамиды:
Очевидно,
что полученная пирамида неправильная
и её нужно исправить. Начнем процедуру
исправления с первого узла, у которого
есть потомки (это узел номер 4) и в каждом
цикле будем уменьшать этот номер на 1.
Т.о. исправлению подлежат поддеревья с
вершинами 4,3,2,1,0.
На рисунке ниже показан результат исправления для вершин 4, 3 и 2:
Исправление вершины 1 потребовало двух ходов:
Исправление вершины 0 потребует уже трёх ходов (второй ход пропущен):
Описанный выше алгоритм представлен ниже в виде функции Build_Max_Heap:
Void Build_Max_Heap(Heap *hp) {
int i;
for(i=(hp->v.sz-1)/2; i >= 0; i--) Max_Heapify(hp, i);
}
Теперь можно приступить к конструированию модульной структуры приложения, использующего абстракцию «пирамида». Прежде всего, сформируем интерфейс, в который поместим описание структуры Heap, а также описания клиентских функций для работы с пирамидами: init_h, Heap_Maximum, Max_Heap_Insert, Max_Heapify, Heap_Extract_Max и Build_Max_Heap.
Как отмечалось ранее, интерфейс помещается в заголовочный файл, который назовем Heap.h :
// Vector.h
typedef struct Heap {Vector v;} Heap;
void init_h(Heap*);
int Heap_Maximum(Heap*, double*);
void Max_Heap_Insert(Heap*, double);
void Max_Heapify(Heap*, int);
int Heap_Extract_Max(Heap*, double*);
void Build_Max_Heap(Heap*);
Вопрос №81. Структура данных «очередь с приоритетом».
Пирамида является очень эффективным средством для реализации очереди с приоритетом – особого вида очереди, в которой в «голове очереди» всегда расположен элемент с максимальным значением ключа (т.е. имеющий максимальный приоритет). Именно этот элемент и будет первым извлекаться из очереди и направляться на обслуживание.
Одна из областей применения очередей с приоритетом— планирование заданий на компьютере, который совместно используется несколькими пользователями. Очередь позволяет следить за заданиями, которые подлежат выполнению, и за их приоритетами. Если задание прервано или завершило свою работу, из очереди с помощью операции Heap_Extract_Max выбирается следующее задание с наибольшим приоритетом. В очередь в любое время можно добавить новое задание, воспользовавшись операцией Max_Heap_Insert.
Связанные списки
Вопрос №82. Однократно связанный (однонаправленный) список: основные характеристики и внутреннее устройство.
Связанный список (linked list) — это структура данных, элементы которой расположены в линейном порядке. Однако, в отличие от массива, в котором этот порядок определяется индексами, порядок в связанном списке поддерживается с помощью указателей. Связанные списки обеспечивают простое и гибкое представление динамических множеств и поддерживают все операции, рассмотренные ранее для массивов.
Списки могут быть разных видов. Простейшим из списков является однократно связанный (однонаправленный - singly linked) список. Будем обозначать вид таких списков L1.
typedef struct Node1 {
double elem; struct Node1 *next; } Node1;
typedef struct List1 { Node1* head; } List1;
Ниже приведена иллюстрация пустого однонаправленного списка и текст функции init_L1 , инициализирующей такой список.
Вопрос №83. Алгоритм включения элемента в однонаправленный список.
Задача включения нового элемента в однонаправленный список может быть решена двумя способами:
включение в голове списка (перед первым элементом) или
включение в хвосте списка (после последнего элемента).
Тексты двух функций, реализующих включение нового элемента в голове списка Insert_head_L1 и в хвосте списка Insert_back_L1.