- •СТРУКТУРЫ ДАННЫХ И АЛГОРИТМЫ
- •Глава 1 Линейные списки
- •Понятие структуры данных
- •Тип указатель
- •Линейный список
- •Линейный однонаправленный (односвязный) список
- •Линейный двунаправленный (двусвязный) список
- •Практическое задание «Линейные списки»
- •Задачи для самостоятельной работы
- •Контрольные вопросы
- •Глава 2 Структуры данных стек и очередь
- •Понятие структуры данных стек и очередь
- •Структура данных стек
- •Структура данных очередь
- •Пример решения задачи с использованием стека
- •Практическое задание «Стеки и очереди»
- •Задачи для самостоятельной работы
- •Контрольные вопросы
- •Глава 3 Древовидные структуры данных
- •Понятие структуры данных дерево
- •Представление структуры бинарное дерево в памяти
- •Обход дерева
- •Основные операции над деревом
- •Ввод дерева
- •Практическое задание «Бинарные деревья»
- •Задачи для самостоятельной работы
- •Контрольные вопросы
- •Глава 4 Структура данных таблица
- •Понятие структуры данных таблица
- •Представление таблицы в памяти
- •Организация хеш-таблиц
- •Выбор функции расстановки
- •Разрешение коллизии
- •Операции над таблицами
- •Практическое задание «Управление таблицами»
- •Задачи для самостоятельной работы
- •Контрольные вопросы
- •Глава 5 Алгоритмы упорядочения данных
- •Понятие операции упорядочения
- •Алгоритмы упорядочения
- •Сортировка слиянием
- •Основные алгоритмы сортировки посредством сравнений
- •Улучшенные алгоритмы сортировки сравнением
- •Практическое задание «Алгоритмы сортировки»
- •Задачи для самостоятельной работы
- •Контрольные вопросы
- •Библиографический список
Структура данных очередь
Очередь с несвязным представлением, отображаемую в статической памяти, будем называть статическая очередь. В статической памяти очередь можно представить как множество данных, которые располагаются в списке последовательно и непрерывно друг за другом и это множество дополнено информацией, которая позволяет вносить изменения в динамическую структуру данных это, прежде всего информация о количестве элементов в очереди. При задании такой очереди вносится ограничение на ее размер.
Статическую очередь можно представить:
а) линейным массивом с двумя указателями: на начало и на конец очереди. Указатель на начало определяет позицию очереди (массива), откуда можно взять элемент, указатель на конец – позицию, куда кладем элемент:
struct queue
{
int beg, end; type data[N];
};
0 |
|
|
K |
|
L |
|
N - 1 beg |
|
end |
|||||
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
data |
|
|
|
|
|
|
|
|
|
|
|
K |
|
L |
|
|
|
|
|
|
|
|
|
|
|
|
|
||
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
начало |
конец |
|
|
|
|
|
|
||||
Задать очередь – это определить переменную типа queue:
queue q;
Для инициализации очереди достаточно определить начальное значение (пустое) указателя на начало и/или конец очереди. Для этого полю end дадим значение, не принадлежащее диапазону от 0 до N - 1 (а полю beg можно задать значение, указывающее на первый элемент
49
очереди – это упростит работу с указателями при включении элемента в очередь): например,
q.beg = 0; q.end = -1;
При включении элемента в очередь увеличивается на единицу значение указателя end, при взятии элемента из очереди значение указателя на начало beg.
Если поле end равно N - 1, то очередь полна. Освободившиеся после взятия элементов из очереди позиции не используются, поэтому возможно мнимое заполнение очереди. Если end равно N - 1 и beg равно 0 – очередь полна, а если beg больше 0, то очередь псевдополна (т. е. из очереди были взяты элементы и в начале массива есть свободные позиции). Обращение к элементу при взятии из очереди: q.data[q.beg], при занесении в очередь: q.data[q.end]. Если в очереди один элемент, то оба указателя показывают на него, т. е. их значения равны, если из очереди взять единственный элемент, она должна стать пустой;
б) линейным массивом с одним указателем на начало или на конец очереди, вторая позиция очереди фиксируется. Например, начало очереди – это первый элемент массива. В таком случае содержимое очереди сдвигается после каждого взятия из нее элемента и уменьшается на единицу поле end. Если поле beg равно 0, а поле end равно N - 1, то очередь полна:
struct queue
{
int end; type data[N];
};
|
0 |
|
K |
|
N - 1 |
|
end |
|
data |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
К |
|
|
|
|
|
|
|
|
|
|
|
начало |
конец |
|
|
|
|
||
|
|
|
|
50 |
|
|
|
|
в) линейным массивом с двумя указателями (аналогично приведенному выше варианту а). При достижении конца массива (последнего элемента) очередь сдвигается на начало массива (к первому элементу), если есть свободные позиции в начале массива. Если поле beg равно 0, а поле end равно N - 1, то очередь полна;
г) циклическая очередь – линейный массив с двумя указателями (аналогично варианту а), в котором при достижении каким-либо указателем конца массива (последнего элемента) значение этого указателя очереди округляется по модулю N, где N – размер массива. Таким образом, в циклической очереди при достижении конца массива (end = N - 1) и наличии свободных позиций в начале (beg > 0), новый элемент помещается в массив на место с индексом 0, при этом указателю end присваивается значение 0.
Очередь будет полна, если поле end = N - 1, а поле beg = 0 или поле end = K, а beg = K + 1. Здесь 0 K N - 1.
0 1 |
|
|
|
|
|
N - 1 |
|||
data |
|
|
|
|
|
|
|
|
|
Э3 |
Э4 |
|
|
• • • |
|
Э1 |
Э2 |
||
|
|
|
|
|
|
|
|
|
|
|
конец |
|
|
начало |
|||||
|
|
|
|
|
|
|
|
||
|
N - 2 |
|
1 |
|
|
|
|
||
|
|
|
|
|
|
|
|
|
|
beg end
Очередь со связным представлением, отображаемую в динамической памяти, будем называть динамическая очередь. В динамической памяти очередь можно представить:
а) линейным односвязным списком с двумя указателями:
struct list
{
type data;
list *next = NULL;
};
51
struct queue
{
list *beg = NULL, *end = NULL;
};
Задать динамическую очередь – это определить переменную типа queue:
queue q;
Для инициализации очереди достаточно указателю на начало q.beg и/или на конец q.end присвоить значение NULL, например: q.beg = NULL. При взятии единственного элемента из очереди она должна стать пустой;
|
|
|
|
начало |
|
|
|
|
конец |
|||
|
|
|
|
|
|
|
|
|
|
|
|
|
beg |
|
|
|
|
|
|
|
|
• • • |
|
|
NULL |
end |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
б) линейным двусвязным циклическим списком с одним указателем:
struct queue
{
type data;
queue *next = NULL, *prev = NULL;
}
• • •
52
Здесь начало (аналогично конец) очереди можно сделать как в начале, так и в конце списка. Задать динамическую очередь – это определить переменную-указатель на элемент типа queue:
queue *q;
Для инициализации очереди достаточно указателю q присвоить значение NULL:
q = NULL;
Основные операции над очередью аналогичны операциям со стеком (с учетом особенностей этой структуры ).
Примеры операций над статической очередью:
struct queue
{
int beg = 0, end = -1; char elem[N];
};
Очередь инициализируется следующим образом:
beg = 0; end = -1;
– операция положить элемент в очередь вида а:
int push(queue *q, char c)
{
if (q->end == n - 1) return 0;
q->end++;
53
q->elem[q->end] = c; return 1;
}
– операция взять элемент из очереди вида а:
int pop(queue *q, char *c)
{
if (q->end == -1) return 0;
*c = q->elem[q->beg]; if (q->beg == q->end)
{
q->beg = 0; q->end = -1;
}
else q->beg++;
return 1;
}
Примеры операций над динамической очередью:
struct list
{
char elem = 0; list *next = NULL;
};
struct queue
{
list *beg = NULL, *end = NULL;
};
54
Очередь инициализируется следующим образом:
beg = NULL;
– операция положить элемент в очередь
void push(queue *q, char c)
{
list *p = new list; p->elem = c; p->next = NULL;
if (q->beg == NULL) q->end = q->beg = p;
else
{
q->end->next = p; q->end = p;
}
}
– операция взять элемент из очереди
int pop(queue *q, char *c)
{
if (q->beg == NULL) return 0;
*c = q->beg->elem; list *p = q->beg; q->beg = q->beg->next; delete p;
return 1;
}
55
