
Лекция-16-Иванченко.ppt
- Количество слайдов: 14
Тема Связанные списки Лекция 08. 11 г. 1
Общие сведения Связанный список (linked list) — это структура данных, элементы которой расположены в линейном порядке. Однако, в отличие от массива, в котором этот порядок определяется индексами, порядок в связанном списке поддерживается с помощью указателей. Связанные списки обеспечивают простое и гибкое представление динамических множеств и поддерживают все операции, рассмотренные ранее для массивов. Списки могут быть разных видов. Простейшим из списков является однократно связанный (однонаправленный - singly linked) список. Будем обозначать вид таких списков L 1. узлы списка head elem next голова elem NULL хвост typedef struct Node 1 { double elem; struct Node 1 *next; } Node 1; typedef struct List 1 { Node 1* head; } List 1; Лекция 08. 11 г. 2
Однонаправленные списки (L 1) Ниже приведена иллюстрация пустого однонаправленного списка и текст функции init_L 1 , инициализирующей такой список. head NULL void init_L 1(List 1 *l) { l->head = NULL; } В дальнейшем полезной будет функция печати элементов списка Print_L 1. void Print_L 1(List 1 *l) { Node 1 *p = l->head; if (p == NULL) printf("List is empty"); while (p) { // цикл обхода узлов от головы к хвосту printf("%5. 1 f ", p->elem); p = p->next; } printf("n--------n"); } Лекция 08. 11 г. 3
Включение элемента в список L 1 Рассмотрим три возможных случая для задачи включения нового элемента в однонаправленный список: Ø включение в голове списка (перед первым элементом), Ø включение в хвосте списка (последнего элемента), Ø включение после заданного узла. q – заданный узел head elem next elem NULL голова elem next новый хвост elem next новый elem NULL новый На следующем слайде представлены тексты функций, реализующих включение нового элемента в голове списка Insert_head_L 1 , в хвосте списка Insert_back_L 1 и после заданного элемента Insert_L 1. Лекция 08. 11 г. 4
Включение элемента в список L 1 На данном слайде представлены тексты функций, реализующих включение нового элемента в голове списка Insert_head_L 1 , в хвосте списка Insert_back_L 1 и после заданного элемента Insert_L 1. void Insert_head_L 1(List 1 *l, double z) { Node 1* p = (Node 1*)calloc(1, sizeof(Node 1)); p->elem = z; p->next = l->head; l->head = p; } void Insert_back_L 1(List 1 *l, double z) { Node 1* p = (Node 1*)calloc(1, sizeof(Node 1)); p->elem = z; p->next = NULL; if (l->head == NULL) {l->head = p; return; } Node 1* q = l->head; while (q->next) q = q->next; q->next = p; } void Insert_L 1(List 1 *l, Node 1 *q, double z) { Node 1* p = (Node 1*)calloc(1, sizeof(Node 1)); p->elem = z; p->next = q->next; q->next = p; } Лекция 08. 11 г. 5
Извлечение (удаление) элемента из списка L 1 Задача извлечения элемента из однонаправленного списка имеет также несколько разновидностей: Ø извлечение первого элемента, Ø извлечение последнего элемента, Ø извлечение элемента, следующего за заданным. head голова elem next elem NULL хвост освобождение памяти head голова elem next хвост elem NULL освобождение памяти head голова elem next q – заданный узел elem next хвост elem NULL освобождение памяти Лекция 08. 11 г. 6
Извлечение элемента из списка L 1 На данном слайде представлены тексты функций, реализующих извлечение элемента в голове списка Extract_head_L 1, в хвосте списка Extract_back_L 1 и после заданного элемента Extract_L 1. int Extract_head_L 1(List 1 *l, double *z) { Node 1 *p = l->head; if (p == NULL) return 0; *z = p->elem; l->head = p->next; free(p); } int Extract_back_L 1(List 1 *l, double *z) { Node 1 *p = l->head, *p 1; if (p == NULL) return 0; if (p->next == NULL) { *z = p->elem; l->head = NULL; free(p); return 1; } for (p 1 = p->next; p 1 ->next; p = p 1, p 1 = p->next); *z = p 1 ->elem; p->next = NULL; free(p 1); return 1; } int Extract_L 1(List 1 *l, Node 1 *q, double *z) { Node 1 *p = q->next; if (p == NULL) return 0; *z = p->elem; q->next = p->next; free(p); return 1; Лекция 08. 11 г. } 7
Тестирование однонаправленного списка Как и для всех ранее рассмотренных структур данных, сформируем файл интерфейса List 1. h и файл реализации List 1. c для списков вида L 1 (тексты не 1 приводятся), а также проведём тестирование: #include
Задача обращения однонаправленного списка Построим алгоритм решения одной простой задачи для однонаправленного списка: перестроить список так , чтобы хвост стал головой и наоборот – голова стала хвостом (т. е. выполнить реверсирование списка). На рисунке показан один цикл обращения. Слева от пунктира – перестроенный список, справа – исходный. elem next r elem next y elem next t elem next r elem next y void reverse_L 1(List 1 *l) { Node 1 *r = l->head, *y, *t; if (r == NULL) return; // список пуст y = r->next; // начальная установка r->next = NULL; // голова стала хвостом while (y) { t = y->next; y->next = r; r = y; y = t; } l->head = r; } Лекция 08. 11 г. 9
Тестирование функции reverse_L 1 #include
Задача сортировки однонаправленного списка Рассмотрим еще одну задачу – сортировка списка вставками. head 10 исходный список 50 20 14 16 N a 50 t фиктивные узлы b 10 N 20 16 N неотсортированный список 14 отсортированный список x a 20 t b 10 50 N 14 16 N неотсортированный список x do { // цикл по неотсортированному списку a->next = t->next; for (x = b; ; x = x->next) // цикл по отсортированному списку if ((x->next == NULL) || (x->next->elem > t->elem)) { t->next = x->next; x->next = t; break; } Лекция 08. 11 г. } while (t = a->next); 11
Исходный код функции sort_L 1 void sort_L 1(List 1 *l) { if ((l->head == NULL) || (l->head->next == NULL)) return; Node 1* a = (Node 1*)calloc(1, sizeof(Node 1)); a->next = l->head->next; Node 1* b = (Node 1*)calloc(1, sizeof(Node 1)); b->next = l->head; l->head->next = NULL; Node 1* x; do { a->next = t->next; for (x = b; ; x = x->next) if ((x->next == NULL) || (x->next->elem > t->elem)) { t->next = x->next; x->next = t; break; } } while (t = a->next); l->head = b->next; } Лекция 08. 11 г. 12
Тестирование функции sort_L 1 #include
Тестирование на случайных числах #include