Вы находитесь на странице: 1из 12

Лабораторная работа №1

Структура данных «стек».

Теоретические сведения.

Стек — это упрощенный вариант связанного списка. Новые узлы могут


добавляться в стек и удаляться из стека только сверху. По этой причине, стек часто
называют структурой вида последним пришел — первым обслужился (LIFO). На стек
ссылаются через указатель на верхний элемент стека. Связывающий элемент в последнем
узле стека установлен равным NULL, чтобы пометить границу стека.
Графическое представление стека с несколькими узлами показано на рис. 1.4. Следует
отметить, что стеки и связанные списки представляются идентично. Разница между
стеком и связанными списками в том, что вставку и удаление в связанных списках можно
выполнять в любом месте, а в стеке только сверху.

Рис. 1.4. Графическая иллюстрация стека

Понятие стека. Операции над стеком


Стек - это последовательный список переменной длины, включение и исключение
элементов из которого выполняются только с одной стороны списка, называемого
вершиной стека. Другие названия стека - «магазин» и очередь, функционирующая по
принципу LIFO (Last - In - First - Out - "последним пришел - первым исключается").
Примеры стека: винтовочный патронный магазин, тупиковый железнодорожный разъезд
для сортировки вагонов.
Основные операции над стеком - включение нового элемента (англ, push -
заталкивать) и исключение элемента из стека (англ, pop - выскакивать).
Полезными могут быть также вспомогательные операции:
• определение текущего числа элементов в стеке;
• очистка стека;
• неразрушающее чтение элемента из вершины стека, которое может быть
реализовано как комбинация основных операций:
x=pop(stackO);
push(stackO,x).
Для наглядности рассмотрим небольшой пример, демонстрирующий принцип
включения элементов в стек и исключения элементов из стека. На рис. 14.1 изображены
следующие состояния стека:
а) пустого;
б-г) после последовательного включения в него элементов с именами 'А', 'В', 'С';
д, е) после последовательного удаления из стека элементов 'С и 'В';
ж) после включения в стек элемента 'D'.

1
Рис. 14.1 Включение и исключение элементов из стека

Стек можно представить, например, в виде стопки книг (элементов), лежащей на


столе. Присвоим каждой книге свое название, например А, В, С, D... Тогда в момент
времени, когда на столе книги отсутствуют, про стек по аналогии можно сказать, что он
пуст, т.е. не содержит ни одного элемента. Если же начинать последовательно класть
книги одну на другую, то получим стопку книг (допустим из п книг), или получим стек, в
котором содержится п элементов, причем вершиной его будет являться элемент п+1.
Удаление элементов из стека осуществляется аналогичным образом, т. е. удаляется
последовательно по одному элементу, начиная с вершины, или по одной книге из стопки.
Реализация стека может быть выполнена на основе массива. Кроме собственно
массива необходимо дополнительно иметь переменную (назовем ее «указатель стека»),
адресующую вершину стека. Под вершиной стека можно понимать либо первый
свободный элемент стека, либо последний записанный. Все равно, какой из этих двух
вариантов выбрать, важно впоследствии при обработке стека строго его придерживаться.
При занесении элемента в стек элемент записывается на место, определяемое
указателем стека, затем этот указатель модифицируется таким образом, чтобы он
указывал на следующий свободный элемент.
Операция выталкивания элемента состоит в модификации указателя стека (в
направлении обратном модификации при включении) и выборке значения, на которое
указывает указатель стека. После выборки элемент, в котором размещалось выбранное
значение, считается свободным.
Инициализация стека
Чтобы организовать стек, объявите структуру:
struct item {
int data;
struct item *next;
};
Структура item имеет два элемента: целую переменную, указатель next на
«свой» тип структуры. Несколько переменных типа item теперь могут формировать
список, связываясь друг с другом с помощью указателя на следующий элемент.
Используйте оператор typedef:

typedef struct item {


int data;
struct item *next;
} Item;
Item *top = NULL;//указатель типа Item (вершина стека)

2
Стек. Добавление элемента(Push)
Item *top = NULL;//указатель на вершину
Если top равен нулю, значит стек пуст. Чтобы начать стек, проверьте его элемент
top. Если он нулевой, выделяйте память для нового элемента типа Item:

if (top == NULL) {
top = (Item *)malloc(sizeof(Item));
top->data = 1;
top->next = NULL;
}

Теперь в стеке есть один элемент, адресуемый указателем top. Указатель next
указывает на NULL (признак конца стека).
При занесении элемента в стек, элемент записывается на место, определяемое
указателем стека, затем этот указатель модифицируется и указывает на следующий
свободный элемент.

Добавим в стек новую информацию:


Item* p;
p = (Item *)malloc(sizeof(Item));
p->data = 2;
p->next = top;
top = p;
После того как программа выделит память для элемента, адресуемого р, она
присвоит переменной data значение 2, а указателю p–>next – адрес вершины стека.
После этого указателю top присваивается значение p, чтобы он вновь указывал на
последний созданный элемент.

Стек. Чтение данных


Каждая структура типа Item связана со следующей с помощью указателя next.
Путем разыменования указателя top можно получить доступ к любому элементу
стека. Выражение top–>data относится к переменной data в последнем элементе
стека. Чтобы получить доступ к другим членам, необходимо использовать
следующие операторы (переменной i присваивается значение элемента data
разных элементов стека):
i = top->data; /* i = 4 */
i = top->next->data; /* i = 3 */
i = top->next->next->data; /* i = 2 */
i = top->next->next->next->data; /* i = 1 */

Однако многоссылочные выражения слишком громоздки и вместо этого, чтобы


«пройтись» по элементам можно использовать цикл:
Item *p = top;
while (p != NULL) {
printf(″%d\n″, p->data);
p = p->next;

3
}
Указатель р инициализируется адресом вершины стека. В цикле функция printf()
отображает значение члена структуры data, а указателю p присваивается адрес
следующей структуры в стеке. Чтобы цикл мог успешно закончиться, последняя
структура должна иметь элемент next, равный NULL.

Стек. Удаление(Pop)
Операция удаления (выталкивания) элемента стека:
Item *p = top;
top = top->next;
free(p);
Указателю р присваивается значение top, затем указателю top присваивается
адрес следующей структуры. Указатель р передаётся в функцию free(), удаляющую
отсоединенный элемент из кучи.

Примеры.

Пример 1
Программа работает с простым стеком целых чисел. Программа выполняет три
действия на выбор:
1) помещает значение в стек (функция push),
2) извлекает значение из стека (функция pop), и
3) завершает работу.

#include<stdlib.h>
#include<malloc.h>
#include<stdio.h>
struct stackNode
{
int data;
struct stackNode *nextPrt;
};
typedef struct stackNode STACKNODE;
typedef STACKNODE * STACKNODEPTR;
void push(STACKNODEPTR *, int);
int pop(STACKNODEPTR *);
int isEmpty(STACKNODEPTR);
void printStack(STACKNODEPTR);
void instruc(void);

void main()
{
STACKNODEPTR stackPtr = NULL;
int choice, value;
instruc();
printf("? ");
scanf_s("%d", &choice);

4
while (choice != 3)
{
switch (choice)
{
case 1:
printf("Enter an integer >> ");
scanf_s("%d", &value);
push(&stackPtr, value);
printStack(stackPtr);
break;
case 2:
if (!isEmpty(stackPtr))
printf("the popped value is %d \n", pop(&stackPtr));
printStack(stackPtr);
break;
default:
printf("Error enter\n");
instruc();
break;
}
printf("? "); scanf_s("%d", &choice);
}
printf("End \n");
}
void instruc(void)
{
printf("l - dobavit\n");
printf("2 - delete\n");
printf("3 - exit\n");
}
void push(STACKNODEPTR *topPtr, int info)
{
STACKNODEPTR newPtr; newPtr = new STACKNODE;
if (newPtr != NULL)
{
newPtr->data = info;
newPtr->nextPrt = *topPtr;
*topPtr = newPtr;
}
else
printf("%d Error not memory\n", info);
}
int pop(STACKNODEPTR *topPtr)
{
STACKNODEPTR tempPtr; int popValue;
tempPtr = *topPtr;
popValue = (*topPtr)->data;
*topPtr = (*topPtr)->nextPrt;
free(tempPtr);
return popValue;
}
void printStack(STACKNODEPTR currentPtr)

5
{
if (currentPtr == NULL)
printf("Error not stack\n\n");
else
{
printf("Stack is :>> \n");
while (currentPtr != NULL)
{
printf("%d -> ", currentPtr->data); currentPtr=currentPtr->nextPrt;
}
printf("\n");
}
}
int isEmpty(STACKNODEPTR topPtr)
{
return topPtr == NULL;
}
В данном примере основные функции, используемые при работе со стеками —
push и pop. Функция push создает новый узел и помещает его на вершину стека. Функция
pop удаляет верхний узел стека, освобождает память, которая была выделена изъятому
узлу, и возвращает изъятое значение.
Программа работает с простым стеком целых чисел. Программа выполняет три
действия на выбор: 1) помещает значение в стек (функция push), 2) изымает значение из
стека (функция pop), и 3) завершает работу.
Функция push помещает новый узел на вершину стека. В выполнении функции
можно выделить три этапа:
1. Создание нового узла посредством вызова malloc, при этом указателю newPtr
присваивается адрес блока выделенной памяти; переменной newPtr -> data присваивается
значение, которое необходимо поместить в стек; и указателю newPtr->nextPtr
присваивается NULL.
2. Указателю newPtr->nextPtr присваивается *topPtr (указатель на вершину стека);
связывающий элемент newPtr теперь указывает на узел, который был верхним до этого.
3. *topPtr присваивается значение newPtr; *topPtr теперь указывает на новую
вершину стека. Операции, включающие в себя *topPtr, меняют значение stackPtr в main.

Рис. 1.5 Графическая иллюстрация выполнения функции push

Наглядное представление о том, как происходит выполнение функции push

6
показано на рис. 1.5. На рис. 1.5, а изображен стек и новый узел перед выполнением
функции push. Пунктирные линии на рис. 1.5, б иллюстрируют шаги 2 и 3 выполнения
функции push, в результате которых узел, содержащий 12, становится новой вершиной
стека.
Функция pop удаляет верхний узел стека. Отметим, что перед тем как вызвать
функцию pop, функция main определяет, не пуст ли стек. В выполнении функции pop
можно выделить пять основных этапов:
1. Указателю tempPtr присваивается *topPtr (tempPtr будет использован для
освобождения ненужной памяти).
2. Переменной pop Value присваивается значение (*topPtr)->data (сохраняется
значение, находившееся в верхнем узле).
3. *topPtr присваивается (*topPtr)->nextPtr (*topPtr присваивается адрес нового
верхнего узла).
4. Освобождается память, на которую указывает tempPtr.
5. Вызывающей функции возвращается значение pop Value (в примере
программы это функция — main).
Выполнение функции pop проиллюстрировано на рис. 1.6. На рис. 1.6, а показано
состояния стека после предыдущей операции push. На рис. 1.6, б выделены указатели
tempPtr, указывающий на первый узел стека, и *topPtr, указывающий на второй узел. Для
освобождения указанной tempPtr памяти вызывается функция free.

Рис. 1.6. Графическая иллюстрация выполнения функции pop

2. Программная реализация стека на основе массива

Пример 2
#include <stdio.h>
#include <stdlib.h>
#include <malloc.h>
#include <conio.h>
////////////////////// Stack for char
typedef struct
{
char * stack;
int head;
int size;
} STACK;
int Init(STACK* Stack, int nSize);
char Pop(STACK* Stack);

7
int Push(STACK* Stack, char cElement);
void Clear(STACK* Stack); int GetSize(STACK* Stack); void Free(STACK* Stack);

////////////////////// Stack for char


int Init(STACK* Stack, int nSize)
{
Stack->stack = (char*)malloc(nSize);
if (Stack->stack == NULL)
return -1;
Stack->size = nSize;
Stack->head = 0; return nSize;
}
char Pop(STACK* Stack)
{
if (Stack->head == 0) return 0; return Stack->stack[--Stack->head];
}

int Push(STACK* Stack, char cElement)


{
if (Stack->head == Stack->size) return -1;
Stack->stack[Stack->head++] = cElement; return 1;
}
void Clear(STACK* Stack)
{
Stack->head = 0;
}
int GetSize(STACK* Stack)
{
return Stack->head;
}
void Free(STACK* Stack)
{
free(Stack->stack);
}

void main()
{
STACK A;
Init(&A, 8);
Push(&A, 'J');
Push(&A, 'F');
char c = Pop(&A);
...

Пример 3
В следующем примере формируется стек, выводится элементы стека, максимальный
элемент и его порядковый номер:

#include <iostream>
#include <conio.h>
using namespace std;

8
struct Stack
{
int info;
Stack *next;
};

int create(Stack **begin);


Stack *t;
void view(Stack *begin);

int main()
{
Stack* begin = NULL;
int i, kol = 0;
int nom_max = kol;
cout << "\n vvedite elementy\n ";
cout << "\n -1 end of enter elements\n ";
while (1)
{
cout << "i= ";
cin >> i;
if (i < 0)
break;
t = new Stack;
t->info = i;
t->next = begin;
begin = t;
kol++;
}

cout << "\n elements stack \n ";


if (begin == NULL){
cout << "Empty stack!";
return 1;
}
while (t != NULL)
{
cout << t->info << endl;
t = t->next;
}
cout << "\n Found MAX \n ";
//поиск макс и порядк ном
if (begin == NULL)
{
cout << "Empty stack!";
return 1;
}
t = begin;
int max = t->info;
do
{
begin = t->next;
kol--;
if (t->info > max) { max = t->info; nom_max = kol; }
delete t;

9
t = begin;
} while (t != NULL);
cout << "\nmax = " << max << "\nnom max = " << nom_max << endl;
//------------
int a=1;
while (a!=0)
{
begin = NULL;
kol = create(&begin);
view(begin);
cout << "\n dla vyhoda-0\n ";
cin >> a;

}
return 0;
}

int create(Stack **begin)


{
int k = 0; int i; do
{
cout << "i= ";
cin>>i;
if (i<0)
{
return k;
}
t = new Stack;
t->info = i;
t->next = *begin;
k++;
*begin = t;
} while (1);
}
void view(Stack *begin)
{
Stack *t = begin;
if (begin == NULL)
{
cout<<"Empty stack"; return;
}
while (t != NULL)
{
cout<<t->info;
t = t->next;
}
}

В результате работы программы и ввода 5 элементов стека получаем:

10
Варианты индивидуальных заданий
Задание 1
1. Создать стек для целых чисел. Максимальный размер стека вводится с экрана.
Создать функции для ввода и вывода элементов стека. Добавить б элементов. Удалить и
вывести на экран 2 элемента.
2. Создать стек для целых (положительных и отрицательных) чисел. Максимальный
размер стека вводится с экрана. Создать функции для ввода и вывода элементов стека.
Ввести с экрана 6 элементов. При вводе чисел в стек попадают только отрицательные
элементы. Вывести все элементы стека.
3. Создать стек для целых чисел. Максимальный размер стека вводится с экрана.
Создать функции для ввода, вывода и определения размера стека. Ввести с экрана 6
элементов. Удалить 2 элемента. Вывести размер стека.
4. Создать стек для целых (положительных и отрицательных) чисел. Максимальный
размер стека вводится с экрана. Создать функции для ввода, выгода и определения
размера стека. Вводить с экрана числа, причем в стек должны добавляться поочередно
положительные и отрицательные числа.
5. Создать стек для символов. Максимальный размер стека вводится с экрана.
Создать функции для ввода и вывода элементов стека. Ввести эталонный символ.
Вводить символы с экрана в стек до встречи эталонного. Вывести все элементы стека.
6. Создать стек для символов. Максимальный размер стека вводится с экрана.
Создать функции для ввода и вывода элементов стека. Добавить символы с экрана в стек.
После добавления 5-го символа перед добавлением удалять элемент из стека.
7. Создать стек для символов. Максимальный размер стека вводится с экрана.
Создать функции для ввода, вывода и определения размера стека. Ввести эталонный
символ. Вводить символы с экрана в стек до встречи эталонного. Вывести размер стека.
8. Создать стек для символов. Максимальный размер стека вводится с экрана.
Создать функции для ввода и вывода элементов стека. Добавлять символы с экрана в
стек. В случае совпадения вводимого символа с вершиной стека вытолкнуть его и
распечатать ее.
9. Создать стек для символов. Максимальный размер стека вводится с экрана.
Создать функции для ввода, вывода и определения размера стека. Вводить символы с
экрана в стек. В случае совпадения вводимого символа с вершиной стека вывести размер
стека.
10. Создать два стека для символов. Максимальный размер стеков вводится с
экрана. Создать функции для ввода и вывода элементов стека. Вводить символы с экрана
в первый стек. В случае совпадения вводимого символа с вершиной стека вводить во
второй стек.
11. Создать два стека для символов. Максимальный размер стеков вводится с

11
экрана. Создать функции для ввода и вывода элементов стека. Вводить символы с экрана
в стеки поочередно.
12. Создать стек для символов и стек для чисел. Максимальный размер стеков
вводится с экрана. Создать функции для ввода и вывода элементов стека. Вводить
символы с экрана. Символ попадает в первый стек, а его численное представление - во
второй.
13. Создать два стека для символов. Максимальный размер стеков вводится с
экрана. Создать функции для ввода и вывода элементов стека. Вводить символы с экрана.
Прописные буквы попадают в первый стек, строчные - во второй, остальные символы
пропускаются.
14. Создать два стека для символов. Максимальный размер стеков вводится с
экрана. Создать функции для ввода и вывода элементов стека. Вводить символы с экрана.
Прописные буквы преобразуются в строчные и попадают в первый стек, строчные
преобразуются в прописные и попадают во второй, остальные символы пропускаются.
15. Создать стек для целых чисел. Максимальный размер стека вводится с экрана.
Создать функции для ввода и вывода элементов стека. Вводить символы с экрана.
Числовое представление символа попадает в стек.

12