Главная страница
Навигация по странице:

  • Двусвязный список

  • Лекции Булатицкий Дмитрий Иванович (во многом по материалам Прасолова А. Н.)


    Скачать 319.62 Kb.
    НазваниеЛекции Булатицкий Дмитрий Иванович (во многом по материалам Прасолова А. Н.)
    Дата11.01.2022
    Размер319.62 Kb.
    Формат файлаdocx
    Имя файлаLecture_Programming_2021_09_01.docx
    ТипЛекции
    #328427
    страница36 из 36
    1   ...   28   29   30   31   32   33   34   35   36

    Классификация ДСД


    По способу обхода (доступа) можно выделить следующие виды ДСД:

    • Список;

    • Стек;

    • Очередь;

    • Очередь с приоритетом;

    • Кольцевая очередь;

    • Ассоциативный массив;

    По количеству связей элементов с «соседними» элементами:

    • Односвязные;

    • Двухсвязные;

    • Многосвязные;

    По характеру связи:

    • Линейные. В линейной динамической структуре данные связываются в цепочку. К линейным структурам относятся списки (односвязные, двухсвязные, кольцевые), стеки, очереди (односторонние, двухсторонние, очереди с приоритетами).

    • Нелинейные (деревья, графы).

    Из всего многообразия ДСД в рамках данной лекции рассмотрим только наиболее распространённые из них.
        1. Cписки


    Списком называется упорядоченное множество, состоящее из переменного числа элементов, к которым применимы операции включения, исключения. Список, отражающий отношения соседства между элементами, называется линейным. Длина списка равна числу элементов, содержащихся в списке, список нулевой длины называется пустым списком. Линейные связные списки являются простейшими динамическими структурами данных.

    Графически связи в списках удобно изображать с помощью стрелок. Если компонента не связана ни с какой другой, то в поле указателя записывают значение, не указывающее ни на какой элемент. Такая ссылка обозначается специальным именем – NULL (nul, nil).

    Двусвязный список характеризуется наличием пары указателей в каждом элементе: на предыдущий элемент и на следующий. Очевидный плюс тут в том, что от данного элемента структуры мы можем пойти в обе стороны. Таким образом упрощаются многие операции. Однако на указатели тратится дополнительная память.

    Разновидностью рассмотренных видов линейных списков является кольцевой список, который может быть организован на основе как односвязного, так и двухсвязного списков. При этом в односвязном списке указатель последнего элемента должен указывать на первый элемент; в двухсвязном списке в первом и последнем элементах соответствующие указатели переопределяются.

    При работе с такими списками несколько упрощаются некоторые процедуры. Однако, при просмотре такого списка следует принять некоторых мер предосторожности, чтобы не попасть в бесконечный цикл.
        1. Cтек (LIFO)


    Стек - такой последовательный список с переменной длиной, включение и исключение элементов из которого выполняются только с одной стороны списка, называемого вершиной стека. Применяются и другие названия стека - магазин и очередь, функционирующая по принципу LIFO (Last - In - First- Out - "последним пришел - первым исключается"). Примеры стека: винтовочный патронный магазин, тупиковый железнодорожный разъезд для сортировки вагонов.

    Основные операции над стеком - включение нового элемента (английское название push - заталкивать) и исключение элемента из стека (англ. pop - выскакивать).

    Полезными могут быть также вспомогательные операции: определение текущего числа элементов в стеке; очистка стека; неразрушающее чтение элемента из вершины стека, которое может быть реализовано, как комбинация основных операций.

    Некоторые авторы рассматривают также операции включения/исключения элементов для середины стека, однако структура, для которой возможны такие операции, не соответствует стеку по определению.
        1. Очередь (Queue, FIFO)


    Очередью FIFO (First - In - First- Out - "первым пришел - первым исключается"). называется такой последовательный список с переменной длиной, в котором включение элементов выполняется только с одной стороны списка (эту сторону часто называют концом или хвостом очереди), а исключение - с другой стороны (называемой началом или головой очереди). Те самые очереди к прилавкам и к кассам, которые мы так не любим, являются типичным бытовым примером очереди FIFO.

    Основные операции над очередью - те же, что и над стеком - включение, исключение, определение размера, очистка, неразрушающее чтение.
        1. Дек (DEQ)


    Дек - особый вид очереди. Дек (от англ. deq - double ended queue,т.е очередь с двумя концами) - это такой последовательный список, в котором как включение, так и исключение элементов может осуществляться с любого из двух концов списка. Частный случай дека - дек с ограниченным входом и дек с ограниченным выходом. Логическая и физическая структуры дека аналогичны логической и физической структуре кольцевой FIFO-очереди. Однако, применительно к деку целесообразно говорить не о начале и конце, а о левом и правом конце.

    Операции над деком: включение элемента справа; включение элемента слева; исключение элемента справа; исключение элемента слева; определение размера; очистка.

    Задачи, требующие структуры дека, встречаются в вычислительной технике и программировании гораздо реже, чем задачи, реализуемые на структуре стека или очереди. Примером дека может быть, например, некий терминал, в который вводятся команды, каждая из которых выполняется какое-то время. Если ввести следующую команду, не дождавшись, пока закончится выполнение предыдущей, то она встанет в очередь и начнет выполняться, как только освободится терминал. Это FIFO очередь. Если же дополнительно ввести операцию отмены последней введенной команды, то получается дек.
        1. Деревья


    Каждая вершина дерева представляет собой структуру, имеющую информационное поле и указатели поддеревья, исходящие из этой вершины. Максимальное количество поддеревьев, сходящихся в одной вершине, называется порядком дерева. Если порядок дерева равен двум, то. Дерево называется бинарным.
      1. Интерфейс и реализация ДСД «Стек»

        1. Интерфейс ДСД «Стек»


    Как было показано ранее, основными операциями со над стеком являются вставка в стек и извлечение из него. К вспомогательным операциям относят:

    • определение текущего числа элементов в стеке или проверка наличия елементов в стеке;

    • очистку стека и неразрушающее чтение элемента из вершины стека;

    • печать стека.

    С учётом необходимости управления динамической памятью необходимо также добавить следующие служебные операции: инициализация и уничтожение стека.

    Следует так проектировать интерфейс, чтобы избавить пользователя от ненужных ему подробностей внутреннего устройства разрабатываемой динамической структуры данных (как, впрочем, и других типов).

    Рассмотрим описание интерфейса стека на примере стека для хранения вещественных чисел. Тип хранимых элементов в дальнейшем может быть адаптирован для других практических задач.

    Поместим описание интерфейса стека в заголовочный файл «StackA.h»:
    #ifndef _STACK_A_H

    #define _STACK_A_H
    typedef struct

    {

    double *A;

    int l;

    int top;

    } StackA;
    int Push(StackA *s, double v);

    double Pop(StackA *s);

    double Peek(StackA const *s);

    int IsEmptyStack(StackA const *s);

    void Clear(StackA *s);

    void InitStack(StackA *s, int l);

    void PrintStack(StackA const *s);

    void Destruct(StackA *s);
    #endif
    Для предотвращения ошибки повторного определения типов при многократном (прямом или опосредованном) включении заголовочного файла обрамим его содержимое директивами условной компиляции.
        1. Реализация стека в массиве


    Рассмотренный в предыдущем пункте интерфейс может быть реализован таким образом, что элементы стека будут храниться в динамическом массиве. Предложенная реализация показана в следующем листинге (поместим его в файл «StackA.c»):
    #include "stackA.h"

    void InitStack(StackA *s, int l)

    {

    if(s->A=(double *)malloc(sizeof(double)*l))

    {

    s->l=l;

    }

    else s->l=-1;

    s->top=-1;

    }

    void PrintStack(StackA const *s)

    {

    int i;

    for(i=0; i<= s->top; i++)

    printf ("%-7.3lf ", (s->A)[i]);

    }

    void Destruct(StackA *s)

    {

    if(s->A) free(s->A);

    s->l=-1;

    s->top=-1;

    }

    int Push(StackA *s, double v)

    {

    if(s->top>=s->l-1) return 0;

    s->top++;

    s->A[s->top]=v;

    return 1;

    }
    double Pop(StackA *s)

    {

    if(s->top>=0)

    return s->A[s->top--];

    else return 1e300;

    }
    double Peek(StackA const *s)

    {

    if(s->top>=0)

    return s->A[s->top];

    else return 1e300;

    }

    void Clear(StackA *s)

    {

    s->top=-1;

    }
    int IsEmptyStack(StackA const *s)

    {

    if(s->top <0) return 1;

    else return 0;

    }

        1. Реализация ДСД «Стек» в виде связного списка


    Иногда удобнее представить список в виде связной структуры. Для возможности в программе одновременно работать со списками различной реализации, переопределим также и интерфейс с другим идентификатором типа.

    Поместим описание интерфейса стека в заголовочный файл «StackL.h»:
    #ifndef _STACK_L_H

    #define _STACK_L_H

    struct LE

    {

    double v;

    struct LE *next;

    };
    typedef struct

    {

    struct LE *top;

    } StackL;

    int Push(StackL *s, double v);

    double Pop(StackL *s);

    double Peek(StackL const *s);

    int IsEmptyStack(StackL const *s);

    void Clear(StackL *s);

    void InitStack(StackL *s);

    void PrintStack(StackL const *s);

    void Destruct(StackL *s);
    #endif
    Реализацию поместим его в файл «StackL.c»:
    #include
    #include "StackL.h"

    void InitStack(StackL *s)

    {

    s->top=NULL;

    }


    int Push(StackL *s, double v)

    {

    struct LE *p=(struct LE *)malloc(sizeof(struct LE));

    if(!p) return 0;

    p->next=s->top;

    p->v=v;

    s->top=p;

    return 1;

    }

    double Pop(StackL *s)

    {

    struct LE *p;

    double x=1e300;

    if(s->top)

    {

    p=s->top;

    s->top=s->top->next;

    x=p->v;

    free(p);

    }

    return x;

    }
    double Peek(StackL const *s)

    {

    if(s->top)

    return s->top->v;

    }

    int IsEmptyStack(StackL const *s)

    {

    return s->top==NULL;

    }
    void Clear(StackL *s)

    {

    while(!IsEmptyStack(s)) Pop(s);

    }
    void PrintStack(StackL const *s)

    {

    struct LE *p;

    for(p=s->top; p; p=p->next)

    printf("%-7.3lf ", p->v);

    }
    void Destruct(StackL *s)

    {

    Clear(s);

    }

    Пример работы со стеком:

    #include

    #include
    #include "stackL.h"
    int main(int argc, char *argv[])

    {

    StackL a, b;

    int i;

    double x;

    InitStack(&a);

    Push(&a, 1.1);

    Push(&a, 2.2);

    printf ("Enter value to push: ");

    scanf("%lf", &x);

    Push(&a, x);

    PrintStack(&a);

    for (i=0; i<5; i++) printf("\n Extracted %-7.3lf\n", Pop(&a));

    PrintStack(&a);

    Destruct(&a);

    system("PAUSE");

    return 0;

    }

    Замечание: для оптимизации работы с памятью и упрощения примера интерфейс стека в массиве рассмотренный ранее при инициализации предполагает дополнительный по сравнению с предложенным в данном примере интерфейсом стека в связном списке. Тем не менее этого можно избежать, выделяя при инициализации стека массив из какого-то предопределённого числа элементов. В дальнейшем, если такой длины будет недостаточно, память можно выделить повторно с помощью realloc, увеличив её объём.
    1. Заключение

      1. Основные направления и тенденции развития языков программирования и технологии конструирования программ


    Потребность в решении более сложных и разнообразных задач. Первые ЭВМ имели ограниченные возможности, следовательно, и программы были простыми. В процессе эволюции вычислительной техники от нее требовалось решение все более сложных и разнообразных задач. Следовательно, язык программирования должен был позволять писать программы для решения этих новых задач. Это способствовало появлению и развитию в языках программирования различных новых технологий. Например, пользуется широкой популярностью технология объектно-ориентированного программирования.

    Программы становились сложнее и больше по объему. Появилось стремление к повышению эффективности процесса создания программ. Поэтому существует тенденция в развитии языков программирования к быстрому написанию программ. Здесь также следует отметить появление множества систем визуального программирования, в какой-то степени облегчающие труд программиста.

    Желание, чтобы программы работали на разных платформах, привело к развитию независимости от ЭВМ языков системного программирования. Языки системного программирования, на которых создаются операционные системы, трансляторы и другие системные программы, развиваются в направлении независимости от ЭВМ. Так, например, большая часть операционных систем написана на языке C, а не на ассемблере. Например, операционная система Unix практически полностью написана на C.

    Большие проекты предусматривают совместный труд множества программистов. В возможности легкой командной работы хорошо себя зарекомендовала технология объектно-ориентированного программирования. Поэтому большинство современных языков программирования поддерживают ООП.

    В общем, языки программирования развиваются в сторону все большей абстракции от реальных машинных команд. И самым очевидным преимуществом здесь является увеличение скорости разработки программы.

    Также повышению скорости разработки программ способствует автоматизация написания программ.

    Среди подходов к автоматизации создания программ можно выделить наиболее распространённый – визуальное программирование.

    Суть визуального программирования заключается в том, что среда разработки берет на себя большую часть рутинной работы по организации инфраструктуры программы для взаимодействия её частей, оставляя программисту работу по визуальному конструированию диалоговых окон и заполнению функций обработки предопределённых событий.

    Получающиеся приложения являются интерактивными системами, в которых для организации взаимодействия между пользователем и программой используются методы (подпрограммы), управляемые событиями.

      1. Основные идеи объектно-ориентированного программирования


    Объектно-ориентированное программирование — это методология программирования, которая основана на представлении программы в виде совокупности объектов, каждый из которых является реализацией определенного класса (типа особого вида), а классы образуют иерархию, основанную на принципах наследуемости. При этом объект характеризуется как совокупностью всех своих свойств и их текущих значений, так и совокупностью допустимых для данного объекта действий.

    Несмотря на то что в различных источниках делается акцент на те или иные особенности внедрения и применения ООП, три основных (базовых) понятия ООП остаются неизменными. К ним относятся:

    • наследование (Inheritance);

    • инкапсуляция (Encapsulation);

    • полиморфизм (Polymorphism).

    Эти понятия как три кита лежат в основе ООП. При процедурном подходе требуется описать каждый шаг, каждое действие алгоритма для достижения конечного результата. В отличие от него объектно-ориентированный подход оставляет за объектом право решать, как отреагировать и что сделать в ответ на поступивший вызов.
    1   ...   28   29   30   31   32   33   34   35   36


    написать администратору сайта