Інформація про навчальний заклад

ВУЗ:
Національний технічний університет України Київський політехнічний інститут
Інститут:
Не вказано
Факультет:
Не вказано
Кафедра:
Не вказано

Інформація про роботу

Рік:
2024
Тип роботи:
Курсова робота
Предмет:
Програмування та алгоритмічні мови

Частина тексту файла (без зображень, графіків і формул):

Міністерство освіти і науки, молоді і спорту України Національний технічний університет України «Київский політехнічний інститут» Навчально-науковий комплекс «Інститут прикладного системного аналізу» Кафедра Системного Проектування Курсова робота З дисципліни «Програмування і алгоритмічні мови» «Обхід дерев» Зміст Вступ 2 Теоретична частина 4 Обробка даних у вершинах дерева 5  Лівий спадний обхід 6  Лівий висхідний обхід 6  Лівий змішаний обхід 7 Приклад лівого спадного обходу LPreorder 10 Перелік використаної літератури 11 Код програми реалізації обходу дерев мовою С. 12 Вступ Ця робота присвячена дослідженню засобів обходу дерев. На сьогоднішній день дерева є найбільш поширеною структурою. Вони використовуються майже в кожній галузі, що стосується програмування, моделювання інформаційних процесів, комунікаційних систем, системного аналізу і систематизації взагалі. Вони популярні серед людей з технічним напрямком занять, і не дарма: дерева забезпечують найшвидші і найоптимальніші умови для знаходження елемента серед інших в певній структурі. Звичайно, існують багато типів дерев, в залежності від мети їх створення, і багато операцій над деревами: створення дерева, включення елемента в дерево, обробка даних у вершинах дерев, видалення елемента з дерева, збереження і відновлення бінарного дерева, знищення бінарного дерева. Але в будь-якому дереві найважливішою операцією все одно залишатиметься пошук (локалізація) елемента в дереві, бо на ній побудовані ледь не всі інші операції. Наприклад, включення елемента в дерево складається з трьох дій: пошуку місця включення, виділення динамічної пам’яті і безпосереднього занесення даних. При чому при пошуку місця для елемента ми повинні впевнитись у тому, що такого елемента ще не існує в цьому дереві. А це означає ще одну операцію обходу. Взагалі, дерева – це така структура, що забезпечує найоптимальніші умови зберігання даних і також найменший час їх пошуку, а в збалансованих деревах взагалі можна знайти елемент зробивши лише один крок. Недарма розв’язанням проблем, пов’язаних з деревами, займаються не тільки національні і державні університети та дослідницькі інститути, а ще й великі комерційні підприємства та фірми. У дерева пошуку, дерева сортування й у взагалі таку область дискретної математики, як дерева вкладено величезну купу грошей, нескінченні роки розумової праці найяскравіших інтелектуалів планети. Проте щоб збалансовані дерева залишалися такими при тому, що в них зберігатимуться величезні об’єми даних, треба винайти ще більш ефективні методи обходу дерев, що займають менше часу і, крім того, ще б по ходу дєла сортували данні у відповідності з принципами, що я наводжу у теоретичній частині. Саме такі задачі поставлені перед нашою сьогоднішньою технічною елітою, і перед нами в тому ж числі. Теоретична частина Обхід дерев найчастіше проходить задля пошуку елемента. А пошук елемента має на меті різні завдання. По-перше, визначити, чи належить шуканий елемент дереву, чи ні. В цьому випадку результатом буде повернення ознаки про наявність елемента чи його відсутність( true or false, 1 чи 0 відповідно). По-друге, пошук з метою вибірки й опрацювання даних елементу, тоді результат пошуку повертається у вигляді адреси вершини. По-третє, пошук з метою видалення елементу, такий пошук часто є лиш частиною повної операції видалення, а не самостійною операцією. Сам алгоритм пошуку залежить від виду дерева. В ідеально збалансованому дереві пошук доводиться проводити обходом вершин в деякій послідовності. Мінімальна довжина пошуку тоді дорівнює 1, максимальна ж – n, ну а середня тоді – n/2. Алгоритм пошуку в бінарному дереві пошуку, як в простому, так і в збалансованому, достатньо простий. Пошук проходить цілеспрямованим рухом вздовж ребер дерева. Якщо ключ пошуку дорівнює ключу у вершині, тоді ключ вважається знайденим і його адреса повертається через параметр-вказівник. Якщо ж ключ пошуку менший від ключа у вершині, тоді ми рухаємося по лівому піддереву, якщо ж навпаки, то тоді по правому піддереву. Якщо ж у черговій вершині рух вниз неможливий (вказівник дорівнює NULL), то це означає, що шуканого ключа в дереві не знайдено – його там немає. Максимальна довжина пошуку дорівнює висоті дерева. Обробка даних у вершинах дерева Розрізняють два види обробки: вибіркову обробку окремої вершини і послідовну обробку всіх вершин. Вибіркова обробка включає в себе пошук елементу, і обробку даних у вершині лише в тому випадку, коли шуканий елемент знайдений. Обробка даних залежить від задачі, що вирішується і може зводитись до вилучення данних з вершини без їх змін, або оновленню даних у вершині дерева. При послідовній обробці проходить так званий обхід дерева. Обхід дерева – процес одноразового відвідування й обробки даних кожної вершини дерева. В залежності від того, в якому напрямку здійснюється рух вздовж гілок дерева і коли обробляються дані у вершинах, шість видів обходів:  Лівий спадний обхід LPreorder – рух згори до низу, від кореневої вершини до листків, реалізується таким алгоритмом: procedure ПРЕФІКСНИЙ ОБХІД(:дерево); begin     Відвідати корінь  дерева ;  if не лист then Нехай – піддерева кореня ;         for i := 1 to k do ПРЕФІКСНИЙ-ОБХІД( ) end  end end. Таким чином проходить обробка даних у вузлі, потім лівий спадний обхід лівого піддерева, і нарешті лівий спадний одхід правого піддерева. В нашому випадку це буде така послідовність вершин: 12, 4, 2, 1, 3, 8, 6, 5, 7, 10, 9, 11, 17, 14, 13, 16, 15, 20, 19, 18, 21. Лівий висхідний обхід LPostorder – рух зліва направо, від самого лівого листка догори до кореня, потім донизу до самого правого листка. Реалізується таким алгоритмом: procedure ПОСТФІКСНИЙ ОБХІД(:дерево); begin     Відвідати корінь  дерева ;  if не лист then Нехай – піддерева кореня ;         for i := 1 to k do ПОСТФІКСНИЙ-ОБХІД( ) end  end end. Таким чином в нас проходить спочатку лівий висхідний обхід лівого піддерева, потім лівий висхідний обхід правого піддерева, і тільки потім обробка даних у вузлі. Цьому алгоритму відповідає така послідовність вершин: 1, 3, 2, 5, 7, 6, 9, 11, 10, 8, 4, 13, 15, 16, 14, 18, 19, 21, 20, 17, 12. Лівий змішаний обхід LІnorder – рух зліва направо, від самого лівого листка догори до кореня, потім донизу до самого правого листка. Реалізується таким алгоритмом: procedure ІНФІКСНИЙ ОБХІД(:дерево); begin     Відвідати корінь  дерева ;  if не лист then Нехай – піддерева кореня ;         for i := 1 to k do ІНФІКСНИЙ-ОБХІД( ) end  end end. Таким чином в нас проходить спочатку лівий змішаний обхід лівого піддерева, потім обробка даних у вузлі, а вже потім – лівий змішаний обхід правого піддерева. Цьому алгоритму в нашому випадку відповідає послідовність вершин: 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21. При всіх обходах рух по гілкам дерева продовжується до тих пір, поки вказівник на вузол дерева не стане нульовим. Застосовуються також і праві обходи дерев: правий спадний RPreorder, правий висхідний RPostorder і правий змішаний RInorder. Їх алгоритми отримуються з алгоритмів лівих обходів шляхом заміни «лівий» на «правий». Тому їх іноді називають зворотніми обходами.  Окрім спадного, висхідного й змішаного обходів, що були вже розглянуті нами, іноді застосовують обхід дерева по рівням. При такому обході вузли дерева відвідуються згори до низу і зліва на право, тобто спочатку корінь дерева, потім вузли першого рівня зліва на право, потім вузли другого рівня зліва на право, і т.д. Функції обходу дерев можуть бути як рекурсивними, так і ітеративними, найбільш просто реалізуються рекурсивні функції перших шести видів обходу, що ми вже розглядали, в них рекурсивний обхід відповідає рекурсивній структурі дерева. В них, природно, використовуються системні стеки. У не рекурсивних функціях доводиться використовувати явний стек, який, як відомо, реалізується правилом LIFO – ввійшов останнім, вийшов першим. Обхід вздовж рівнів (корінь – перший рівень – другий рівень – третій рівень –…) не відповідає рекурсивній структурі дерева (корінь – піддерево кореня – піддерево піддерева –…). В не рекурсивній функції обходу по рівням замість стеку доводиться використовувати чергу типу FIFO – ввійшов першим, першим вийшов. Очевидно, ця функція обробки даних у вершинах дерева залежить від конкретного застосування дерева. В той же час алгоритми обходів дерев залишаються стабільними і працюють незалежно від конкретної структури. Бінарні дерева пошуку можуть бути використані не тільки відповідно зі своїм прямим призначенням – пошуком елементів, а й для сортування даних. Проводячи лівий змішаний обхід бінарного дерева пошуку і розташовуючи дані з вузлів у тому порядку, в якому вони зустрічаються, отримаємо послідовну множину елементів-ключів дерева, впорядковану за зростанням. Аналогічно, правий змішаний обхід дерева дасть нам послідовну множину елементів-ключів дерева, впорядковану за спаданням. Тому інколи про дерева пошуку кажуть як про дерева сортування. При роботі з деревами виникає ситуація, коли до певних елементів звертаються частіше, до інших – рідше. Трапляються ситуації, коли необхідно повторно звертатись до елементів, до яких були зверненні найсвіжіші запити. Час досягнення цих елементів було б логічно скоротити, помістивши їх ближче до кореня дерева. Для цього достатньо помістити в корінь дерева вузол, до якого був звернений останній запит. Таким чином всі елементи, що використовувались найчастіше, опиняться в такій близькості від кореня, яка б відповідала частоті їх застосування. Це буде зручно для користувача, якщо, звісно, він ставить перед собою таку мету. Якщо ж використання елементів залежить напряму від теорії вірогідності (кожен елемент використовуватиметься вдруге не раніше, ніж всі інші елементи використаються вперше, або взагалі один раз), то має логічне місце пересунення «використаних» вершин в листки або взагалі їх видалення. Приклад лівого спадного обходу LPreorder:      Перелік використаної літератури Н. Вирт Алгоритми і структури даных. М.: Мир, 1989. Глава 4.5 (С. 272-286) Г. М. Адельсон-Вельский, Е. М. Ландис. Один алгоритм організації інформації // Доповіді АН СССР. 1962. Т. 146, № 2. C. 263–266. Б. С. Хусаїнов. Структури і алгоритми обробки даних. Приклади мовою Сі, Вид-во: Фінанси й статистика, 2004, (С. 155-196), 464 стр. Ф. В. Лактионов, В. А. Філіпов, О. В. Андреев //Журнал наукових публікацій аспірантів и докторантів. 2008. № 4. С. 207-210. http://algolist.ru/ Код програми реалізації обходу дерев мовою С. #include <cstdlib> #include <iostream> #include <cstring> using namespace std; struct record {//Структура узла дерева int data;//Ключ record *leftPtr, *rightPtr;//Левый и правый указатели int bal;//Признак сбалансированности дерева }; void instructions(void);//Список возможных действий int insert(int, record **, int *);//Добавление ключа в дерево void del(int, record **,int *);//Удаление ключа из дерева void balanceL(record **, int *); void balanceR(record **, int *); void delbal(record**, record **, int*); void rotL(record**);//Ротация влево void retR(record**);//Ротация вправо void preorder(record*);//Нисходящий обход void inorder(record*);//Смешанный обход void postorder(record*);//Восходящий обход int main() { cout<<"Coursework. Work with AVL-trees."<<endl; cout<<"Made by Andrew Grushko, KPI, IASA, SP-SAPR, first course."<<endl; record *root=NULL; int *h=new int,choice,key; instructions(); cin>>choice; if(!choice) { cout<<"Error in choice. Run again"<<endl; return 0; } while(choice!=3) { switch(choice) { case 1: cout<<"Type a number to insert into the tree: "; cin>>key; insert(key,&root,h);//Добавление ключа в дерево inorder(root);//Вывод на экран break; case 2: cout<<"Type a number to delete from the tree: "; cin>>key; del(key,&root,h);//Удаления ключа из дерева inorder(root); break; default: cout<<"Invalid choice."<<endl<<"? "; break; } instructions(); cin>>choice; } cout<<"End of run."<<endl; return 0; } //=================================inctructions()========================= void instructions() { cout<<"Enter your choice:\n\ 1 to insert a number into the tree.\n\ 2 to delete a number from the tree.\n\ 3 to end."<<endl; cout<<"? "; } //=================================insert()=============================== int insert(int key, record **t, int *h) { record *t1, *t2, *T; int ret=0; T=*t; if(T==NULL) {//дерево пустое T=new record; T->data=key; T->bal=0; *h=1; T->leftPtr=T->rightPtr=NULL; }//поиск ключа в дереве else if(key<T->data) { insert(key, &T->leftPtr,h); if(*h) { switch(T->bal) { case 1:T->bal=0; *h=0; break; case 0:T->bal=-1;break; case -1: { t1=T->leftPtr; if(t1->bal==-1){//LL-поворот T->leftPtr=t1->rightPtr; t1->rightPtr=T; T->bal=0; T=t1; } else {//Двукратный LR-поворот t2=t1->rightPtr; t1->rightPtr=t2->leftPtr; t2->leftPtr=t1; T->leftPtr=t2->rightPtr; t2->rightPtr=T; if(t2->bal==-1) T->bal=1; else T->bal=0; if(t2->bal==1) t1->bal=-1; else t1->bal=0; T=t2; } T->bal=0;*h=0; } } } } else if(key>T->data) { insert(key,&T->rightPtr,h); if(*h==1) switch (T->bal){ case -1: T->bal=0; *h=0; break; case 0: T->bal=1; break; case 1: {//балансировка t1=T->rightPtr; if(t1->bal==1) {//RR-поворот T->rightPtr=t1->leftPtr; t1->leftPtr=T; T->bal=0; T=t1; } else {//двукратный RL-поворот t2=t1->leftPtr; t1->leftPtr=t2->rightPtr; t2->rightPtr=t1; T->rightPtr=t2->leftPtr; t2->leftPtr=T; if(t2->bal==1) T->bal=-1; else T->bal=0; if(t2->bal==-1) t1->bal=1; else t1->bal=0; T=t2; } T->bal=0; *h=0; } } } else { cout<<"/nDuplicate key: "<<key<<endl; ret=-1; } *t=T; return ret; } //====================================del()=============================== void del(int key, record **T, int *h) { record *t, *K; t=*T; if(*T==NULL) { cout<<"Element "<<key<<" is not found"<<endl; *h=0; } else if(key<t->data) { del(key,&t->leftPtr,h); if(*h) balanceL(&t, h); } else if(key>t->data) { del(key,&t->rightPtr,h); if(*h) balanceR(&t,h); } else {//Удаление K=t;//Узел найден if(K->rightPtr==NULL) {//Имеет только левую ветвь t=K->leftPtr; delete K; *h=1; } else if(K->leftPtr==NULL) {//Имеет только правую ветвь t=K->rightPtr; delete K; *h=1; } else { delbal(&K->leftPtr,&K,h); if(*h) balanceL(&t,h); } } *T=t; } //====================================delbal()============================ void delbal(record **R, record **K, int *h) { record *r, *k; r=*R; k=*K; if(r->rightPtr!=NULL) {//спускаемся по правым ветвям до конца delbal(&r->rightPtr,&k,h); if(*h) balanceR(R,h); } else { k->data=r->data; *R=r->leftPtr; delete r; *h=1; } } //====================================balanceL()========================== void balanceL(record **t, int *h)//h=1 => Левая ветвь короче { record *t1, *t2, *T; int b1, b2; T=*t; switch(T->bal) { case -1:T->bal=0; break; case 0:T->bal=1; *h=0; break; case 1: {//балансировка t1=T->rightPtr; b1=t1->bal; if(b1>=0) {//однократный RR-поворот T->rightPtr=t1->leftPtr; t1->leftPtr=T; if(!b1) { T->bal=1; t1->bal=-1; *h=0; } else { T->bal=0; t1->bal=0; } T=t1; } else {//двукратный RL-поворот t2=t1->leftPtr; b2=t2->bal; t1->leftPtr=t2->rightPtr; t2->rightPtr=t1; T->rightPtr=t2->leftPtr; t2->leftPtr=T; if(b2==1) T->bal=-1; else T->bal=0; if(b2==-1) t1->bal=1; else t1->bal=0; T=t2; t2->bal=0; } } } *t=T; } //====================================balanceR()========================== void balanceR(record **t, int *h)//h=-1 =>Правая ветвь короче { record *t1, *t2, *T; int b1, b2; T=*t; switch(T->bal) { case 1:T->bal=0; break; case 0:T->bal=-1; *h=0; break; case -1: {//балансировка t1=T->leftPtr; b1=t1->bal; if(b1<=0) {//однократный LL-поворот T->leftPtr=t1->rightPtr; t1->rightPtr=T; if(!b1) { T->bal=-1; t1->bal=1; *h=0; } else { T->bal=0; t1->bal=0; } T=t1; } else {//Двукратный LR-поворот t2=t1->rightPtr; b2=t2->bal; t1->rightPtr=t2->leftPtr; t2->leftPtr=t1; T->leftPtr=t2->rightPtr; t2->rightPtr=T; if(b2==-1) T->bal=1; else if(b2==1) T->bal=-1; else T->bal=0; T=t2; t2->bal=0; } } } *t=T; } //====================================preorder()========================== void preorder(record *T) { if(T){ cout<<T->data<<", "; preorder(T->leftPtr); preorder(T->rightPtr); } } //====================================inorder()=========================== void inorder(record *T) { if(T){ inorder(T->leftPtr); cout<<T->data<<", "; inorder(T->rightPtr); } } //====================================postorder()========================= void postorder(record *T) { if(T){ postorder(T->leftPtr); postorder(T->rightPtr); cout<<T->data<<", "; } }
Антиботан аватар за замовчуванням

08.02.2013 00:02-

Коментарі

Ви не можете залишити коментар. Для цього, будь ласка, увійдіть або зареєструйтесь.

Ділись своїми роботами та отримуй миттєві бонуси!

Маєш корисні навчальні матеріали, які припадають пилом на твоєму комп'ютері? Розрахункові, лабораторні, практичні чи контрольні роботи — завантажуй їх прямо зараз і одразу отримуй бали на свій рахунок! Заархівуй всі файли в один .zip (до 100 МБ) або завантажуй кожен файл окремо. Внесок у спільноту – це легкий спосіб допомогти іншим та отримати додаткові можливості на сайті. Твої старі роботи можуть приносити тобі нові нагороди!
Нічого не вибрано
0%

Оголошення від адміністратора

Антиботан аватар за замовчуванням

Подякувати Студентському архіву довільною сумою

Admin

26.02.2023 12:38

Дякуємо, що користуєтесь нашим архівом!