Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

Зубенко, Омельчук - Програмування. Поглиблений курс

.pdf
Скачиваний:
51
Добавлен:
07.03.2016
Размер:
4.72 Mб
Скачать

Розділ IV. АЛГОРИТМИ

Itemptr getPrevElem(Itemptr start, int n)

{

Itemptr prev=NULL; while(start) { if(n==start->data) return prev; prev=start;

start=start->next;

}

return NULL; /*елемент не знайдено*/

}

/*пошук елемента за його значенням*/ Itemptr search(Itemptr start, int n)

{

while(start) { if(n==start->data) return start; start=start->next;

}

return NULL; /*елемент не знайдено*/

}

/*пошук останнього елемента*/ Itemptr getLast(Itemptr start)

{

while(start->next) start=start->next; return start;

}

Розглянемо вставлення елементів у лінійний список. Нехай потріб- но вставити елемент у кінець списку.

Приклад 4.15. Вставлення й вилучення елемента в лінійному списку:

Лістинг.

/*додавання нового елемента зі значенням n у кінець списку*/ Itemptr addItem (Itemptr start, int n)

{

Itemptr p;

p=(Itemptr)malloc(sizeof(Item)); p->data=n;

p->next=NULL; if(!start) start=p; else{

501

ПРОГРАМУВАННЯ

start=getLast(start); start->next=p;

}

return p;

}

/*вилучення елемента з однозв'язного списку, розташованого за елементом prevItem; якщо видаляється голова списку, то prevItem=NULL; результат 1 – видалення відбулося, 0 – видалення не виконане*/

int deleteItem(Itemptr start,Itemptr prevItem)

{

if(start) /*список не порожній*/

{

Itemptr p; /*видаляється голова списку*/

if(!prevItem) { p=start; start=start->next; free(p);

return 1;

}

else{

p=prevItem->next;

if(p) /*попередник не останній*/{ prevItem->next=p->next; free(p);

return 1; } else return 0;

}

}

else return 0;

}

При роботі з упорядкованим списком зручно додавати елемент до списку відразу в потрібну позицію.

Приклад 4.16. Пошук елемента у відсортованому списку:

Лістинг.

/*пошук елемента за значенням у відсортованому списку; результат – адреса його попередника; NULL – якщо елемент перший у списку або його не знайдено*/

Itemptr getPrevPositionElem(Itemptr start, int n)

{

502

Розділ IV. АЛГОРИТМИ

Itemptr prev=NULL; while((start)&&(start->data<n)) { prev=start;

start=start->next;

}

return prev; /*елемент не знайдено*/

}

/*вставлення елемента зі значенням у відсортований список у потрібну позицію*/

void addElemSort(Itemptr start,int n)

{

Itemptr prev=getPrevPositionElem(start, n); Itemptr p=(Itemptr)malloc(sizeof(Item)); p->data=n;

p->next=NULL;

if(!prev) /*елемент перший*/{ p->next=start;

start=p;

} else /*не перший*/{ p->next=prev->next; prev->next=p;

}

}

Недоліком однозв'язного списку є те, що в ньому не можна рухатися у зворотному напрямку, тому частіше використовують двозв'язний список.

Двозв'язний список. У двозв'язному списку кожен елемент інфо- рмації містить покажчики на наступний і попередній елементи. Та- ким списком можна пересуватися в довільному напрямку як у по- чаток, так і в кінець:

head

prev

 

prev

prev

дані

дані

дані

дані

next

 

next

next

Проілюструємо роботу з двозв'язним списком. Приклад 4.17. Оголошення двозв'язного списку:

Лістинг.

503

ПРОГРАМУВАННЯ

typedef struct item { int data;

struct item *next; /*покажчик на наступний елемент*/ struct item *prev; /*покажчик на попередній елемент*/ } Item;

typedef Item *Itemptr;

Itemptr HeadList; /*голова списку*/

Послідовне перебирання елементів двозв'язного списку здійснюєть- ся так само просто, як і у випадку однозв'язного. Наведена нижче функція виводить на екран усі імена зі списку.

Приклад 4.18. Виведення значень елементів двозв'язного списку:

Лістинг.

/* Виведення значень елементів списку*/ void display(Itemptr start)

{

while(start) { printf("%d\t", start->data); start=start->next;

}

printf("\n");

}

При виклику функції display параметр start задає голову списку. Після цього функція переходить до наступного елемента, на який ука- зує поле next. Процес припиняється, коли next дорівнює нулю.

Розглянемо приклад функції пошуку за ключовим полем data (на відміну від однозв'язного списку потрібно шукати не попередника, а сам елемент).

Приклад 4.19. Пошук, додавання й вилучення елемента:

Лістинг.

/*пошук елемента за ключем; результат – NULL, якщо елемент не знайдено*/

Itemptr getElem(Itemptr start, int n)

{

while(start) { if(n==start->data) return start;

start=start->next;

504

Розділ IV. АЛГОРИТМИ

}

return NULL; /*елемент не знайдено*/

}

/*пошук останнього елемента*/ Itemptr getLast(Itemptr start)

{

while(start->next) start=start->next; return start;

}

/*пошук елемента за значенням у відсортованому списку; результат – адреса його попередника; NULL – якщо елемент у голові списку або його не знайдено*/

Itemptr getPrevPositionElem(Itemptr start, int n)

{

Itemptr prev=NULL; while((start)&&(start->data<n)) { prev=start;

start=start->next;

}

return prev; /*елемент не знайдено*/

}

/*додавання нового елемента зі значенням n у кінець двозв'язного списку*/

Itemptr addItem (Itemptr start, int n)

{

Itemptr p; p=(Itemptr)malloc(sizeof(Item)); p->data=n;

p->next=NULL; p->prev=NULL; if(!start) start=p; else{

start=getLast(start); start->next=p; p->prev=start;

}

return p;

}

/*Видалення елементів із двозв'язного списку спрощується порівняно з однозв'язним*/

/*видалення елемента із двозв'язного списку за його адресою; результат 1 – видалення відбулося, 0 – видалення не виконане*/

505

ПРОГРАМУВАННЯ

int deleteItem(Itemptr start,Itemptr delItem)

{

if((start)&&(delItem)) /*список не порожній*/

{

Itemptr p, p1; p=delItem->prev; p1=delItem->next;

if(p) /*елемент не перший*/ p->next=p1;

else start=p1;

if(p1) /*елемент не останній*/ p1->prev=p;

free(delItem); return 1;

}

else return 0;

}

Розглянемо роботу з упорядкованим двозв'язним списком. Приклад 4.20. Пошук і додавання елемента:

Лістинг.

/*пошук елемента за значенням у відсортованому списку; результат – адреса його попередника; NULL – якщо елемент у голові списку або його не знайдено*/

Itemptr getPrevPositionElem(Itemptr start, int n)

{

Itemptr prev=NULL; while((start)&&(start->data<n)) { prev=start;

start=start->next;

}

return prev; /*елемент не знайдено*/

}

/*вставлення елемента зі значенням у відсортований список у потрібну позицію*/

void addElemSort(Itemptr start,int n)

{

Itemptr prev=getPrevPositionElem(start, n); Itemptr p=(Itemptr)malloc(sizeof(Item)); p->data=n;

p->next=NULL;

if(!prev) /*елемент перший*/{ p->next=start;

506

Розділ IV. АЛГОРИТМИ

p->prev=prev; start=p;

} else /*не перший*/{ p->next=prev->next; p->prev=prev; prev->next=p;

}

}

Загальні списки. На відміну від лінійних списків, елементами яких є атоми, елементами загальних списків є інші списки, тобто це списки списків. Для їхнього подання використовують загальні зв'яза- ні списки. Структури цих списків мають два поля. Перше інформа- ційне містить покажчик на голову певного списку, а друге поле зв'язку покажчик на наступний елемент списку. У разі відсутності останнього значення цього поля NULL.

Приклад опису загального списку:

struct Item {

/*інформаційне поле*/

void *data;

struct Item *next; /*покажчик на наступний елемент*/ };

typedef struct Item *Itemptr; /*введення імені Itemptr для типу покажчиків на структуру Item*/

Itemptr HeadList; /*покажчик на голову списку*/

Завдяки суто індуктивній природі загальних списків основу їх об- робки становлять рекурсивні функції.

4.3.2. СТЕКИ

Стеки визначені в підрозд. 1.4.4 як кортежі однотипних елемен- тів із відповідними операціями на них. Нагадаємо, що операціями зі стеками є: read читання вершини, pop усунення вершини, push(c) – додавання в кінець стеку нової компоненти, empty пере- вірка стеку на порожність. У реалізаціях стеків перші дві операції об'єднуються в одну з ім'ям pop.

Стек реалізують у вигляді масиву або лінійного списку. Перший варі- ант наведено на рис. 4.1. Цілий масив int stack[MAX] подає елементи

стеку, а змінна-індекс int p його вершину, а саме наступний за верши- ною елемент масиву. При такій реалізації p==0 означає, що стек порожній.

507

ПРОГРАМУВАННЯ

push

pop

el

el

p

el

el

el

el

Stack{0}

Рис. 4.1

Приклад 4.21. Стек цілих чисел у вигляді масиву:

Лістинг.

int stack[MAX];

int p=0; /*вершина стеку*/

/*функція empty – перевірка стеку на порожність*/ int empty ()

{return p<=0;}

/*функція push додає елемент у кінець стеку*/ void push(int i)

{

if(p>=MAX) {

printf("Стек повний\n"); return;

}

stack[p]=i;

p++;

}

/*функція pop знімає елемент із вершини стеку і повертає його як значення*/

int pop(void)

{

if (empty ()) {

printf("Стек порожній\n"); return 0;

}

508

Розділ IV. АЛГОРИТМИ

p--;

return stack[p];

}

Нижче наведені різні варіанти обробки стеку:

Початковий вміст

Функція та її

Результат

Вміст стеку

стеку

аргумент

після операції

 

NULL (порожній)

push(A)

void

A

A

push(B)

void

B A

B A

push(C)

void

C B A

C B A

pop()

C

B A

B A

pop()

B

A

A

push(D)

void

D A

D A

empty()

0

D A

D A

pop()

D

A

A

pop()

A

NULL

NULL

empty()

1

NULL

Стек можна реалізувати за допомогою лінійного списку однотип- них структур, в якому функції pop(), push() та empty() працюють із головою списку, а покажчик вершини стеку встановлено на неї. При- кладом застосування такого стеку може бути перетворення арифме- тичного виразу на форму ОПЗ

Приклад 4.22. Перетворення виразу на форму ОПЗ:

Лістинг.

#include <stdio.h> #include <stdlib.h>

/*Опис стpуктуpи (елемента стеку операцій)*/ typedef struct st{

char c;

struct st *next; } St;

typedef St *Stptr;

Stptr push(Stptr, char); char pop(Stptr*);

int PRIOR(char); int main()

{

509

ПРОГРАМУВАННЯ

struct st *OPERS=NULL; char a[80], outstring[80]; int k, point;

do{

puts("Введіть вираз, що завершується '='):"); fflush(stdin);

/*Введення арифметичного виразу*/ gets(a);

k=point=0;

/*Повторюємо, поки не '=' */ while(a[k]!='\0'&&a[k]!='=')

{

/*Якщо черговий символ – ')', то виштовхуємо зі стеку у вихідний рядок*/

if(a[k]==')') {

/*усі знаки операцій до найближчої дужки, що відкрива-

ється*/

while((OPERS->c)!='(') outstring[point++]=pop(&OPERS);

/*Видаляємо зі стеку дужку, що відкривається*/ pop(&OPERS);

}

/*Якщо черговий символ – літера, то переписуємо її у вихідний рядок*/

if(a[k]>='a'&&a[k]<='z') outstring[point++]=a[k]; /*Якщо черговий символ – '(', то заносимо його у стек*/ if(a[k]=='(') OPERS=push(OPERS, '('); if(a[k]=='+'||a[k]=='-'||a[k]=='/'||a[k]=='*') /*Якщо черговий символ – знак операції, то: */

{/*якщо стек порожній, то записуємо в нього операцію*/ if(OPERS==NULL) OPERS=push(OPERS, a[k]);

/*якщо не порожній*/ else

/*якщо пpіоpитет нової операції більший за пpіоpитет операції на вершині стеку, то заносимо нову операцію у стек*/

if(PRIOR(OPERS->c)<PRIOR(a[k])) OPERS=push(OPERS, a[k]);

/*якщо пpіоpитет менше, то переписуємо у вихідний рядок усі операції з більшим чи pівним пpіоpитетом*/

else { while((OPERS!=NULL)&&(PRIOR(OPERS->c)>=PRIOR(a[k])))

outstring[point++]=pop(&OPERS); /*записуємо у стек нову операцію*/

510

Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]