26. 11. 2017 Списки та рекурсія Рекурсія —























26.11.2017 Списки та рекурсія Рекурсія — це не щось надскладне, а просто ще один засіб програмування, яким можна користуватися успішно або зловживати, як і всім іншим. Д. Баррон
Списки та рекурсія Список елементів множини A : порожній список < > - є списком;
Списки та рекурсія Розглянемо приклади рекурсивних та ітеративних функцій для обробки зв`язного представлення списків.
Представлення даних typedef struct Node {int dat; Node *next;} Listn, *Listp; !!! Не єдиний можливий спосіб визначити потрібні типи даних.
Побудова списку (ітерація) //побудова списку з N елементів n, n-1, n-2,..., 2, 1 Listp lform(int n){ Listp p, t = NULL; for (int i = 1; i <= n; i++){ p = new Node; p->dat = i; p->next = t; t = p; } return p;}
Побудова списку (рекурсія) //побудова списку з N елементів n, n-1, n-2,..., 2, 1 Listp lform_r(int n){ if (n){ Listp p = new Node; p->dat = n; p->next = lform_r(n-1); return p; } return NULL; }
Відображення списку (ітерація) //виведення списку void lprint(Listp p){ while (p){ cout << p->dat << ' '; p = p->next; } cout << endl; }
Відображення списку (рекурсія) //рекурсивне виведення списку void lprint_r(Listp p){ if (p){ cout << p->dat << ' '; lprint_r(p->next); } } //cout << endl; ???
Вилучення списку (ітерація) //знищення списку void ldel(Listp p){ Listp t; while (p){ t = p; p = p->next; delete t; } }
Вилучення списку (рекурсія) //рекурсивне знищення списку void ldel_r(Listp p){ if (p){ ldel_r(p->next); delete p; } }
Кількість елементів списку (ітерація) //кількість елементів списку int numb(Listp p){ int n = 0; while (p){ n++; p = p->next; } return n; }
Кількість елементів списку (рекурсія) //рекурсивно кількість елементів списку int numb_r(Listp p){ if (p) return (1 + numb_r(p->next)); return 0; }
Пошук у списку (ітерація) //пошук елемента за ключем Listp find(Listp const pbeg, int d){ Listp t = pbeg; while (t){ if (t->dat == d) break; t = t->next; } return t; }
Пошук у списку (рекурсія) //рекурсивний пошук елемента за ключем Listp find_r(Listp const pbeg, int d){ if (pbeg) { if (pbeg->dat == d) return pbeg; else return(find_r(pbeg->next, d)); } else return(pbeg); }
Списки та рекурсія Порівняємо наведені рішення: ??? “телефонна аналогія”
Зауваження Розглянули один із способів зв`язного представлення списків - лінійні “однозв`язні” списки - зберігаються зв`язки поточного елемента з наступним. Існують інші способи організації елементів списку (“двозв`язні”, “циклічні”, “з головою”, з інформацією в елементах, або ззовні елементів, а також комбінація вказаних). Обрання способу організації елементів списку повинно враховувати потрібні дії зі списком.
Підсумки Розглянуті лінійні “однозв`язні” списки надають можливості для гнучкого представлення та ефективної обробки різноманітних послідовностей даних. Розглянули найпростіші дії зі списком з використанням як ітеративного, так й рекурсивного підходу. Аналогічним чином можна було б реалізувати й інші дії зі списком, а також при іншій організації списку.
Поради Обирати найбільш адекватний спосіб збереження для списку. Писати “прозорі”, добре структуровані програми. Здійснювати внутрішнє документування у програмі за допомогою коментарів. Не зловживати “трюкачеством”. Не зловживати рекурсією. Розумним чином “форматувати” текст програми.
Задачі Написати функції для визначення кількості додатних чисел у елементах списку: нерекурсивну; рекурсивну. Написати функції розташування елементів нециклічного однозв’зного списку у зворотному порядку (обернення списку): у новому списку; без побудови нового списку.
Задачі Написати рекурсивні функції для виконання розглянутих операцій зі списками: виведення елементів списку (розглянути порядок: починаючи з початку, з кінця списку); додавання в кінець; додавання в середину списку. Написати рекурсивну та нерекурсивну функції для визначення суми елементів числової послідовності, представленої зв`язним списком.
Задачі Написати функції додавання та вилучення рядка зі списку за умови, що елемент списку зберігає вказівник на рядок і кількість m його повторень. При першому додаванні рядка полю m присвоюється 1, потім m збільшується на 1 при додаванні і зменшується на 1 при вилученні, а при вилученні рядка з m = 1 знищується елемент, що подає рядок.
Задачі Гра - “лічилка” задається натуральними N і M. Числа 1, …, N розташовано колом, і від числа 1 починається відлік; M-е число вилучається. Відлік поновлюється з елемента, наступного за вилученим, і так до вилучення всіх чисел. Написати підпрограму друку за N і M послідовності чисел, які вилучаються, наприклад, при N = 4 і M = 3 це буде 3, 2, 4, 1. Пропонується реалізувати рішення з використан-ням зв’зного списку, масиву, рекурентного співвідношення.