Креневич А.П.,Обвінцев О.В.C в задачах і прикладах
..pdfРозділ 10. Рекурсивні структури даних |
171 |
p -> next = NULL; if (empty(*pq))
pq -> bg = p; else
pq -> en -> next = p; pq -> en = p;
}
int take(queue *pq){ if (empty(*pq)) {
cout << "Queue is empty\n"; return 0;
}
else { qref p; int n;
p = pq -> bg;
n = pq -> bg -> d;
pq -> bg = pq -> bg -> next;
if (pq -> bg == NULL) pq -> en = NULL; free(p);
return n;
}
}
Програма, що виконує завдання:
#include |
<stdlib.h> |
|
#include |
<iostream.h> |
|
#include |
"queue.h" |
|
void main(){ |
|
|
queue q; |
// Черга |
|
int n; |
|
// Змінна для поточного числа |
randomize(); |
|
|
init(&q); |
// Ініціалізація черги |
|
do { |
|
|
//Генерація випадкових чисел у змінну n n = random(100);
//Якщо випадкове число дорівнює нулю, то
//припиняємо додавання елементів до черги if (n == 0) break;
add(&q, n);
}while (1);
//Поки черга залишається непорожньою,
//виводимо елементи черги на екран
172 |
С у задачах і прикладах |
|
while (!empty(q))
cout << take(&q) << " "; cout << "\n";
}
Приклад 10.3. Описати модуль для роботи з деком цілих чисел, у якому реалізувати базові операції роботи з деком. Використовуючи цей модуль, реалізувати задачу: з клавіатури ввести послідовність цілих чисел, що завершується 0. На базі послідовності побудувати динамічну структуру, в якій спочатку йтимуть від'ємні числа, а потім – додатні.
Розв'язок. Модуль для роботи з деком складається з двох файлів – файл заголовків deque.h і файл реалізації deque.cpp.
/* deque.h */
//Вказівник на елемент дека typedef struct delem *dref;
//Елемент дека
struct delem { int d;
dref next, prev;
}; // Дек
typedef struct{ dref bg, en;
} deque;
/* Почати роботу */
extern void init(deque *pd);
//Функція визначення, чи є дек порожнім extern int empty(deque d);
//Додати елемент до початку дека
extern void put_bg(deque *pd, int n); // Взяти елемент з початку дека
extern int get_bg(deque *pd, int *pn);
//Додати елемент до кінця дека extern void put_en(deque *pd, int n);
//Взяти елемент з кінця дека
extern int get_en(deque *pd, int *pn);
Файл реалізації:
/* deque.cpp*/ #include <stdio.h> #include <stdlib.h> #include <iostream.h> #include "deque.h" void init(deque *pd){
pd -> bg = NULL;
Розділ 10. Рекурсивні структури даних |
173 |
pd -> en = NULL;
}
int empty(deque d){ return (d.bg == NULL);
}
void put_bg(deque *pd, int n){ dref p;
p = (dref) malloc(sizeof(struct delem)); p -> d = n;
p -> prev = NULL;
p -> next = pd -> bg;
if (pd -> bg == NULL) pd -> en = p; else pd -> bg -> prev = p;
pd -> bg = p;
}
int get_bg(deque *pd, int *pn) { dref p;
if (empty(*pd)){
cout << "get_bg: Дек порожнiй\n";
// якщо дек порожній, get_bg повертає 0 return 0;
}
p = pd -> bg; *pn = p -> d;
pd -> bg = pd -> bg -> next;
if (pd -> bg == NULL) pd -> en = NULL; else pd -> bg -> prev = NULL;
free(p); return 1;
}
void put_en(deque *pd, int n){ dref p;
p = (dref) malloc(sizeof(struct delem)); p -> d = n;
p -> next = NULL;
p -> prev = pd -> en;
if (empty(*pd)) pd -> bg = p; else pd -> en -> next = p;
pd -> en = p;
}
int get_en(deque *pd, int *pn) { dref p;
if (empty(*pd)){
cout << "get_en: Дек порожнiй\n";
174 |
С у задачах і прикладах |
|
// якщо дек порожній, get_en повертає 0 return 0;
}
p = pd -> en; *pn = p -> d;
pd -> en = pd -> en -> prev;
if (pd -> en == NULL) pd -> bg = NULL; else pd -> en -> next = NULL;
free(p); return 1;
}
Для реалізації завдання потрібно аналізувати введене з клавіатури число: якщо воно від'ємне, то додавати його у початок дека, а якщо додатне – у кінець дека. Отже, програма матиме вигляд:
#include <iostream.h> #include "deque.h" void main(){
deque q; int n; init(&q); do {
cin >> n; if (n == 0)
break;
else if (n < 0)
put_bg(&q, n); // додаємо у початок дека else
put_en(&q, n); // додаємо у кінець дека
} while (1);
while (!empty(q)){ get_bg(&q, &n); cout << n << " ";
}
}
Приклад 10.4. Описати модуль, у якому реалізувати базові операції для роботи з класичним списком.
Розв'язок. Опишемо модуль у складі двох файлів – файл заголовків classlst.h і файл реалізації classlst.cpp.
/* classlst.h */ |
|
typedef struct lelem *list; |
/* Список */ |
struct lelem { |
/* Елемент списку */ |
int d; |
|
list next; |
|
}; |
|
Розділ 10. Рекурсивні структури даних |
175 |
/* Почати роботу */
extern void init(list *pl);
/* Операція визначення, чи порожній список */ extern int empty(list l);
/* Додати елемент у початок списку */ extern void add(list *pl, int n);
/* Голова списку */ extern int head(list l); /* Хвіст списку */
extern list tail(list l);
Файл реалізації
/* classlst.cpp */ #include <stdlib.h> #include "classlst.h" void init(list *pl){
*pl = NULL;
}
int empty(list l){ return l == NULL;
}
void add(list *pl, int n){ list p;
p = (list) malloc(sizeof(struct lelem)); p -> d = n;
p -> next = *pl; *pl = p;
}
int head(list l){ if (!empty(l))
return l -> d; else
return 0;
}
list tail(list l){ if (empty(l))
return NULL; else
return l -> next;
}
Як тестову програму реалізуємо таку задачу. У список записати n випадкових чисел, вивести їх на екран, після чого знищити список.
/* uselst.cpp */ #include <iostream.h> #include <stdlib.h>
176 |
С у задачах і прикладах |
|
#include "classlst.h"
//Заповнення списку n випадковими числами void RandomFillList(list *pl, int n){
randomize();
for (int i = 1; i <= n; i++) add(pl, random(10));
}
//Рекурсивна підпрограма
//виведення списку на екран
void ShowList(list l){ if (!empty(l)){
cout << head(l) << " "; ShowList(tail(l));
} |
|
} |
|
void main(){ |
|
list l; int n; |
|
init(&l); |
// Ініціалізація списку |
cout << "n=? "; cin >> n; RandomFillList(&l, n); ShowList(l);
}
Зауваження. Класичний список може одночасно використовуватися кількома динамічними структурами. Саме тому список має існувати до завершення виконання програми. Через це у програмі не описано підпрограму для видалення списку з пам'яті. Якщо ж у цьому стане потреба, то підпрограму видалення списку з пам'яті можна описати так:
// Підпрограма видалення списку з пам'яті void Remove(list *pl){
list p;
while (!empty(*pl)){
p = *pl; *pl = p -> next; free(p);
}
Приклад 10.5. Описати модуль, у якому реалізувати базові операції для роботи зі списком з поточним елементом.
Розв'язок. Опишемо модуль у складі двох файлів – файл заголовків listcur.h і файл реалізації listcur.cpp. У задачі розглядатимемо список цілих чисел.
/* listcur.h */
//Вказівник на елемент списку typedef struct lelem *lref;
//Елемент списку
struct lelem { int d;
Розділ 10. Рекурсивні структури даних |
177 |
lref next;
};
//Список - складається з двох полів:
//beg - вказівник на перший елемент списку
//cur – вказівник на поточний елемент списку typedef struct {
lref beg, cur; } list;
//Почати роботу
extern void init(list *pl);
//Визначення, чи порожній залишок списку extern int emp_end(list l);
//Зробити поточним перший елемент списку extern void first(list *pl);
//Перейти до наступного елемента
extern void next(list *pl);
//Отримати поточний елемент extern int current(list l);
//Вставити новий елемент у список перед поточним extern void insert(list *pl, int n);
//Видалити поточний елемент у списку
extern void del_cur(list *pl);
Файл реалізації
/* listcur.cpp */ #include <iostream.h> #include <stdlib.h> #include "listcur.h" void init(list *pl){
pl -> beg = NULL; pl -> cur = NULL;
}
int emp_end(list l){ return l.cur == NULL;
}
void first(list *pl){
pl -> cur = pl -> beg;
}
void next(list *pl){ if (!emp_end(*pl)){
pl -> cur = pl -> cur -> next;
}
}
int current(list l){ if (!emp_end(l))
178 |
С у задачах і прикладах |
|
return l.cur -> d; else
return 0;
}
//Допоміжна функція, що повертає елемент
//списку, що стоїть перед поточним
lref find_prev(list l){ lref p;
if (l.beg == l.cur) p = NULL; else {
p = l.beg;
while (p -> next != l.cur) p = p -> next;
}
return p;
}
void insert(list *pl, int n){ lref p;
p = (lref) malloc(sizeof(struct lelem)); p -> d = n;
p -> next = pl -> cur; if(pl -> beg == pl -> cur)
pl -> beg = p; else
find_prev(*pl) -> next = p;
}
void del_cur(list *pl){ lref p;
if(pl -> cur == NULL){
cout << "del_cur: список порожній\n";
}
else {
p = pl -> cur;
if (pl -> beg == p)
pl -> beg = pl -> cur -> next; else
find_prev(*pl) -> next = pl -> cur -> next; pl -> cur = pl -> cur -> next;
free(p);
}
}
Приклад 10.6. Описати модуль, у якому реалізувати базові операції для роботи з кільцевим списком.
Розділ 10. Рекурсивні структури даних |
179 |
Розв'язок. Опишемо модуль у складі двох файлів – файл заголовків
rlist.h і файл реалізації rlist.cpp. |
|
/* rlist.h */ |
|
typedef struct relem *rlist; |
/* Список */ |
struct relem { |
/* Елемент списку */ |
int d; |
|
rlist next; |
|
}; |
|
// Почати роботу |
|
extern void init(rlist *pr); |
|
// Довжина списку |
|
extern int len(rlist r); |
|
//Перейти до наступного елемента extern void next(rlist *pr);
//Поточний елемент
extern int current(rlist r);
//Вставити новий елемент у список перед поточним extern void insert(rlist *pr, int n);
//Видалити поточний елемент у списку
extern void del_cur(rlist *pr);
Файл реалізації:
/* rlist.cpp */ #include <iostream.h> #include <stdlib.h> #include "rlist.h" void init(rlist *pr){
*pr = NULL;
}
int len(rlist r){ int i = 0; rlist p;
p = r;
if (p != NULL) do {
p = p -> next; i++;
} while (p != r); return i;
}
void next(rlist *pr){ if (*pr != NULL)
*pr = (*pr) -> next; else
cout << "next: список порожній\n";
}
180 |
С у задачах і прикладах |
|
int current(rlist r){ if (r != NULL)
return r -> d; else {
cout << "current: список порожній\n"; return 0;
}
}
//Допоміжна підпрограма, що повертає попередній
//елемент у списку
rlist find_prev(rlist r){ rlist p;
if (r == NULL) p = NULL; else {
p = r;
while (p -> next != r) p = p -> next;
}
return p;
}
void insert(rlist *pr, int n){ rlist p;
p = (rlist) malloc(sizeof(struct relem)); p -> d = n;
p -> next = *pr; if(len(*pr) == 0) p -> next = p;
else
find_prev(*pr) -> next = p; *pr = p;
}
void del_cur(rlist *pr){ rlist p;
if(*pr == NULL)
cout << "del_cur: список порожній\n"; else {
p = *pr;
if (len(*pr) == 1) *pr = NULL;
else
find_prev(*pr) -> next = (*pr) -> next; *pr = (*pr) -> next;
free(p);
}
}