lecture_10

advertisement
Списки
Лекция 10
Списки
Список – структура данных, представляющая собой
конечную последовательность элементов.
Элемент списка:
Данные
Связь
• Односвязные (в том числе циклические )
• Двусвязные (в том числе циклические )
• Иерархические
Односвязные списки
Односвязный список – это список, у элементов которого
существует связь, указывающая на следующий элемент
списка ( односторонняя связь).
Голова
Хвост
Односвязный список
struct Tlist {
char word[256]; //область данных; можно: char* word;
int count;
struct Tlist *next; //ссылка на следующий узел
};
struct Tlist * Head = NULL; //голова списка
struct Tlist * new_el(char newWord[]) {
struct Tlist * p=(struct Tlist*) malloc(
sizeof(struct Tlist));
if(p){
strcpy(p->word, newWord); //записать слово
p->count = 1; //счетчик слов == 1
p->next = NULL; //следующего узла нет
}
return p; //результат функции – адрес элемента
}
Создание первого элемента списка
struct Tlist *head=NULL;
Struct Tlist *p;
p = (struct Tlist*) malloc(sizeof(struct Tlist));
p->data = 5;
p->next = NULL;
head = p;
head
p
5
NULL
Списки смежностей
1
2
2
4
1
2
3
5
3
4
5
3
5
4
4
NULL
5
1
3
4
struct Ie_list{
struct Ie_list *next;
struct Tlist *simpleList;
};
struct
Ie_list *list;
list=(struct Ie_list*)malloc(sizeof(struct Ie_list));
If (list) {
list->next=NULL;
list->simpleList = new_el(“hello!”);
}
Вставка элемента в односвязный список после элемента
с адресом prev
typedef
struct Tlist *Node; //указатель на элемент
void AddAfter (Node prev, char data[]) {
Node cur = new_el(data);
if (cur) {
cur ->next = prev->next;
prev->next = cur;
}
}
Вставка элемента в голову односвязного списка
Node AddFirst (Node Head, char data[]) {
Node cur = new_el(data);
if(cur) cur ->next = Head;
return cur;
}
Удаление элемента из односвязного списка
Проход по списку
Node p = Head;
while (p) { // пока не дошли
p = p->next; // переходим
}
до конца (p!= NULL)
к следующему
Поиск элемента в списке
Node Find (Node Head, char s[]) {
Node q = Head; //лишняя переменная,
while (q && strcmp(q->word, s))
q = q->next;
return q;
}
достаточно
Head
Циклические списки
Циклический список – это список, в котором связь
последнего элемента указывает на первый или
один из других элементов этого списка.
head
Задача: Для заданного односвязного списка определить,
является ли он циклическим.
Преобразовывать список нельзя.
Двусвязные списки
Двусвязные списки – это списки, элементы которых
имеют по две связи, указывающие на предыдущий
и следующий элементы.
NULL
head
typedef struct list {
int data;
struct list *prev;
struct list *next;
} List;
NULL
Двусвязный список
struct Tlist2 {
char word[256]; //область данных
struct Tlist2 *next; //ссылка на следующий узел
struct Tlist2 *prev; //ссылка на предыдущий узел
};
struct Tlist2 * Head = NULL; // голова списка
struct Tlist2* new_el_2(char newWord[]) {
struct Tlist2 * p=(struct Tlist2 *) malloc(
sizeof(struct Tlist2));
if(p){
strcpy(p->word, newWord); //записать слово
p->next = NULL; //следующего узла нет
p->prev = NULL; //предыдущего узла нет
}
return p; //результат функции – адрес элемента
}
Удаление элемента после элемента с адресом cur
q = cur->next;
cur->next = q -> next;
cur->next->next->prev = cur;
free(q);
Вставка элемента в двусвязный список
после элемента с адресом pr
cur = new_el_2(data);
If cur) {
cur ->next = pr->next;
pr->next->prev = cur;
pr->next = cur;
cur->prev = pr;
}
Иерархические списки
Это списки, значениями элементов которых являются
указатели на другие списки (подсписки).
NULL
head
Линейный список
- это множество, состоящее из n (n≥0) узлов (элементов) X[1],
X[2], … , X[n], структурные свойства которого ограничены
линейным (одномерным) относительным положением
узлов (элементов), т.е. следующими условиями:
• если n > 0, то X[1] – первый узел;
• если 1 < k < n,
то k-му узлу X[k] предшествует узел X[k-1],
а за узлом X[k] следует узел X[k+1];
• X[n] – последний узел.
Операции над линейными списками
1.
Получить доступ к k-му элементу списка, проанализировать
и/или изменить значения его полей.
2. Включить новый узел перед k- м.
3. Исключить k-й узел.
4. Объединить два или более линейных списков в один.
5. Разбить линейный список на два или более линейных
списков.
6. Сделать копию линейного списка.
7. Определить количество узлов.
8. Выполнить сортировку в возрастающем порядке по
некоторым значениям полей в узлах.
9. Найти в списке узел с заданным значением в некотором поле.
10. … и т.д.
Не все операции нужны одновременно!
=>
Будем различать типы линейных списков по
набору главных операций, которые над
ними выполняются.
Стек
- это линейный список, в котором все включения и
исключения (и всякий доступ) делаются на одном конце
списка (вершина стека)
Верх
Включить или
исключить
Второй сверху
Третий сверху
Четвертый сверху
Низ
Операции работы со стеками
1. makenull (S) – делает стек S пустым
2. create(S) – создает стек
3. top (S) – выдает значение верхнего элемента стека, не
удаляя его
4. pop(S) – выдает значение верхнего элемента стека и
удаляет его из стека
5. push(x, S) – помещает в стек S новый элемент со
значением x
6. empty (S) - если стек пуст, то функция возвращает 1
(истина), иначе – 0 (ложь).
Стеки
•
•
•
•
•
•
push-down список
реверсивная память
гнездовая память
магазин
LIFO (last-in-first-out)
список йо-йо
В современных компьютерах стек используется для
• размещения локальных переменных;
• размещения параметров процедуры или функции;
• сохранения адреса возврата (по какому адресу надо
вернуться из процедуры);
• временного хранения данных.
На стек выделяется ограниченная область памяти.
При каждом вызове процедуры в стек добавляются новые
элементы. Поэтому если вложенных вызовов будет много,
стек переполнится.
Опасной в отношении переполнения стека является рекурсия.
Реализация стека
strict Tlist {
int data;
struct Tlist * next;
}
typedef struct Tlist * Stack1; // в этом случае работа такая же, как
// с односвязным списком
typedef struct stack {struct Tlist * top; } Stack;
void makenull (Stack *S){
struct Tlist *p;
while (S->top) {
p = S->top;
S->top = p->next;
free(p);
}
}
Stack * create ()
{ Stack *S;
S = (Stack *)malloc(sizeof(Stack));
S->top = NULL;
return S;
}
int top (Stack *S) {
if (S->top)
return (S->top->data);
else
return 0; //здесь может быть реакция на
//ошибку – обращение к пустому стеку
}
int pop(Stack *S){
int a;
struct Tlist *p;
p = S->top;
a = p->data;
S-> top = p->next;
free(p);
return a;
}
void push(int a, Stack *S){ // return NULL - ?
struct Tlist *p;
p =(struct Tlist *)malloc(sizeof(struct Tlist));
if(p) { p->data = a;
p->next = S-> top;
S->top = p ;
}
}
int empty (Stack *S)
{
return (S->top == NULL);
}
Очередь
- это линейный список, в котором
все включения производятся на одном конце списка,
все исключения – на другом его конце
Исключить
Начало
Включить
Второй
Третий
Четвертый
Конец
Очереди
• FIFO (first-in-first-out) –первый вошел,
первый вышел
Операции работы с очередями
1. makenull (Q) – делает очередь Q пустой
2. create(Q) – создает очередь
3. first (Q) – выдает значение первого элемента очереди, не
удаляя его
4. get(Q)/outqueue(Q) – выдает значение первого элемента
очереди и удаляет его из очереди
5. put(x, Q)/inqueue(x, Q) – помещает в конец очереди Q
новый элемент со значением x
6. empty (Q) - если очередь пуста, то функция возвращает 1
(истина), иначе – 0 (ложь).
Реализация очереди
struct Tlist {
into data;
struct Tlist * next;
}
typedef struct queue
{ struct Tlist *first;
struct Tlist *end;
} Queue;
Дек (double-ended queue)
очередь с двумя концами
- это линейный список, в котором все
включения и исключения производятся на
обоих концах списка
Включить
или исключить
Левый
конец
Второй
слева
Включить или
исключить
Третий
слева или
справа
Второй
справа
Правый
конец
Download