Структуры и алгоритмы компьютерной обработки данных

Динамические структуры данных: однонаправленные и двунаправленные списки

Показывать лекцию целиком

Цель лекции: изучить понятия, классификацию и объявление списков, особенности доступа к данным и работу с памятью при использовании однонаправленных и двунаправленных списков, научиться решать задачи с использованием списков на языке C++.

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

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

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

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

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

  • однонаправленные (односвязные) списки;
  • двунаправленные (двусвязные) списки;
  • циклические (кольцевые) списки.
  • В основном они отличаются видом взаимосвязи элементов и/или допустимыми операциями.

    Однонаправленные (односвязные) списки

    Наиболее простой динамической структурой является однонаправленный список, элементами которого служат объекты структурного типа.

    ). В последнем элементе указатель на следующий элемент равен NULL.

    (рис 29.1) Линейный однонаправленный список

    Описание простейшего элемента такого списка выглядит следующим образом:

    struct имя_типа { информационное поле; адресное поле; };

    где информационное поле – это поле любого, ранее объявленного или стандартного, типа;

    адресное поле – это указатель на объект того же типа, что и определяемая структура, в него записывается адрес следующего элемента списка.

    Например:

    struct Node {
                 int key;//информационное поле
                 Node*next;//адресное поле
                };

    Информационных полей может быть несколько.

    Например:

    struct point {
                  char*name;//информационное поле
                  int age;//информационное поле
                  point*next;//адресное поле
                 };

    Каждый элемент списка содержит ключ, который идентифицирует этот элемент. Ключ обычно бывает либо целым числом, либо строкой.

    Основными операциями, осуществляемыми с однонаправленными списками, являются:

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

    Рассмотрим подробнее каждую из приведенных операций.

    Для описания алгоритмов этих основных операций используется следующее объявление:

    struct Single_List {//структура данных
                        int Data; //информационное поле
                        Single_List *Next; //адресное поле
                       };
    . . . . . . . . . . 
    Single_List *Head; //указатель на первый элемент списка
    . . . . . . . . . . 
    Single_List *Current; 
    //указатель на текущий элемент списка (при необходимости)

    Создание однонаправленного списка

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

    //создание однонаправленного списка (добавления в конец)
    void Make_Single_List(int n,Single_List** Head){
      if (n > 0) {
        (*Head) = new Single_List();
        //выделяем память под новый элемент
        cout << "Введите значение ";
        cin >> (*Head)->Data;
        //вводим значение информационного поля
        (*Head)->Next=NULL;//обнуление адресного поля
         Make_Single_List(n-1,((*Head)->Next));
      }
    }

    Печать (просмотр) однонаправленного списка

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

    //печать однонаправленного списка
    void Print_Single_List(Single_List* Head) {
      if (Head != NULL) {
        cout << Head->Data << "\t";
        Print_Single_List(Head->Next);
        //переход к следующему элементу
      }
      else cout << "\n";
    }

    Вставка элемента в однонаправленный список

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

    (рис 29.2) Вставка элемента в однонаправленный список
    /*вставка элемента с заданным номером в однонаправленный список*/
    Single_List* Insert_Item_Single_List(Single_List* Head, 
          int Number, int DataItem){ 
      Number--;
      Single_List *NewItem=new(Single_List);
      NewItem->Data=DataItem; 
      NewItem->Next = NULL;
      if (Head == NULL) {//список пуст
        Head = NewItem;//создаем первый элемент списка
      }
      else {//список не пуст
        Single_List *Current=Head;
        for(int i=1; i < Number  Current->Next!=NULL; i++)
        Current=Current->Next;
        if (Number == 0){
        //вставляем новый элемент на первое место
          NewItem->Next = Head;
          Head = NewItem;
        }
        else {//вставляем новый элемент на непервое место
          if (Current->Next != NULL) 
          	NewItem->Next = Current->Next;
          Current->Next = NewItem;
        }
      }
      return Head; 
    }

    Удаление элемента из однонаправленного списка

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

    Алгоритмы удаления первого и последующих элементов списка отличаются друг от друга. Поэтому в функции, реализующей данную операцию, осуществляется проверка, какой элемент удаляется. Далее реализуется соответствующий алгоритм удаления (рис 29.3).

    (рис 29.3) Удаление элемента из однонаправленного списка
    /*удаление элемента с заданным номером из однонаправленного списка*/
    Single_List* Delete_Item_Single_List(Single_List* Head, 
          int Number){
      Single_List *ptr;//вспомогательный указатель
      Single_List *Current = Head;
      for (int i = 1; i < Number  Current != NULL; i++)
        Current = Current->Next;
      if (Current != NULL){//проверка на корректность
        if (Current == Head){//удаляем первый элемент
          Head = Head->Next;
          delete(Current);
          Current = Head;
        }
        else {//удаляем непервый элемент
          ptr = Head;
          while (ptr->Next != Current)
            ptr = ptr->Next; 
          ptr->Next = Current->Next;
          delete(Current);
          Current=ptr;
        }
      }
      return Head;
    }

    Поиск элемента в однонаправленном списке

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

    //поиск элемента в однонаправленном списке
    bool Find_Item_Single_List(Single_List* Head, int DataItem){
      Single_List *ptr; //вспомогательным указатель
      ptr = Head;
      while (ptr != NULL){//пока не конец списка
        if (DataItem == ptr->Data) return true; 
        else ptr = ptr->Next;
      }
      return false;
    }

    Удаление однонаправленного списка

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

    /*освобождение памяти, выделенной под однонаправленный список*/
    void Delete_Single_List(Single_List* Head){
      if (Head != NULL){
        Delete_Single_List(Head->Next);
        delete Head;
      }
    }

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

    Двунаправленные (двусвязные) списки

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

    ). При этом два соседних элемента должны содержать взаимные ссылки друг на друга.

    В таком списке каждый элемент (кроме первого и последнего) связан с предыдущим и следующим за ним элементами. Каждый элемент двунаправленного списка имеет два поля с указателями: одно поле содержит ссылку на следующий элемент, другое поле – ссылку на предыдущий элемент и третье поле – информационное. Наличие ссылок на следующее звено и на предыдущее позволяет двигаться по списку от каждого звена в любом направлении: от звена к концу списка или от звена к началу списка, поэтому такой список называют двунаправленным.

    (рис 29.4) Двунаправленный список

    Описание простейшего элемента такого списка выглядит следующим образом:

    struct имя_типа {
                     информационное поле;
                     адресное поле 1;
                     адресное поле 2;
                    };

    где информационное поле – это поле любого, ранее объявленного или стандартного, типа;

    адресное поле 1 – это указатель на объект того же типа, что и определяемая структура, в него записывается адрес следующего элемента списка ;

    адресное поле 2 – это указатель на объект того же типа, что и определяемая структура, в него записывается адрес предыдущего элемента списка.

    Например:

    struct list { 
                  type elem ;
                  list *next, *pred ;
                 }
    list *headlist ;

    где type – тип информационного поля элемента списка;

    *next, *pred – указатели на следующий и предыдущий элементы этой структуры соответственно.

    Переменная-указатель headlist задает список как единый программный объект, ее значение – указатель на первый (или заглавный) элемент списка.

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

    Рассмотрим основные операции, осуществляемые с двунаправленными списками, такие как:

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

    Для описания алгоритмов этих основных операций используется следующее объявление:

    struct Double_List {//структура данных
                        int Data; //информационное поле
                        Double_List *Next, //адресное поле
                                    *Prior; //адресное поле
                       };
    . . . . . . . . . . 
    Double_List *Head; //указатель на первый элемент списка
    . . . . . . . . . . 
    Double_List *Current; 
    //указатель на текущий элемент списка (при необходимости)

    Создание двунаправленного списка

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

    //создание двунаправленного списка (добавления в конец)
    void Make_Double_List(int n,Double_List** Head,
             Double_List* Prior){
      if (n > 0) {
        (*Head) = new Double_List();
         //выделяем память под новый элемент
        cout << "Введите значение ";
        cin >> (*Head)->Data;
        //вводим значение информационного поля
        (*Head)->Prior = Prior;
        (*Head)->Next=NULL;//обнуление адресного поля
        Make_Double_List(n-1,((*Head)->Next),(*Head));
      }
      else (*Head) = NULL;
    }

    Печать (просмотр) двунаправленного списка

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

    //печать двунаправленного списка
    void Print_Double_List(Double_List* Head) {
      if (Head != NULL) {
        cout << Head->Data << "\t";
        Print_Double_List(Head->Next);
        //переход к следующему элементу
      }
      else cout << "\n";
    }

    Вставка элемента в двунаправленный список

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

    (рис 29.5) Добавление элемента в двунаправленный список
    //вставка элемента с заданным номером в двунаправленный список
    Double_List* Insert_Item_Double_List(Double_List* Head, 
          int Number, int DataItem){ 
      Number--;
      Double_List *NewItem=new(Double_List);
      NewItem->Data=DataItem; 
      NewItem->Prior=NULL;
      NewItem->Next = NULL;
      if (Head == NULL) {//список пуст
        Head = NewItem;
      }
      else {//список не пуст
        Double_List *Current=Head;
        for(int i=1; i < Number  Current->Next!=NULL; i++)
        Current=Current->Next;
        if (Number == 0){
        //вставляем новый элемент на первое место
          NewItem->Next = Head;
          Head->Prior = NewItem;
          Head = NewItem;
        }
        else {//вставляем новый элемент на непервое место
          if (Current->Next != NULL) Current->Next->Prior = NewItem;
          NewItem->Next = Current->Next;
          Current->Next = NewItem;
          	NewItem->Prior = Current;
          Current = NewItem;
        }
      }
      return Head; 
    }

    Удаление элемента из двунаправленного списка

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

    (рис 29.6) Удаление элемента из двунаправленного списка
    /*удаление элемента с заданным номером из двунаправленного списка*/
    Double_List* Delete_Item_Double_List(Double_List* Head, 
          int Number){
      Double_List *ptr;//вспомогательный указатель
      Double_List *Current = Head;
      for (int i = 1; i < Number  Current != NULL; i++)
        Current = Current->Next;
      if (Current != NULL){//проверка на корректность
        if (Current->Prior == NULL){//удаляем первый элемент
          Head = Head->Next;
          delete(Current);
          Head->Prior = NULL;
          Current = Head;
        }
        else {//удаляем непервый элемент
          if (Current->Next == NULL) {
          //удаляем последний элемент
            Current->Prior->Next = NULL;
            delete(Current);
            Current = Head;
          }
          else {//удаляем непервый и непоследний элемент
            ptr = Current->Next;
            Current->Prior->Next =Current->Next;
            Current->Next->Prior =Current->Prior;
            delete(Current);
            Current = ptr;
          }
        }
      }
      return Head;
    }

    Поиск элемента в двунаправленном списке

    Операция поиска элемента в двунаправленном списке реализуется абсолютно аналогично соответствующей функции для однонаправленного списка. Поиск элемента в двунаправленном списке можно вести:

    а) просматривая элементы от начала к концу списка;

    б) просматривая элементы от конца списка к началу;

    в) просматривая список в обоих направлениях одновременно: от начала к середине списка и от конца к середине (учитывая, что элементов в списке может быть четное или нечетное количество).

    //поиск элемента в двунаправленном списке
    bool Find_Item_Double_List(Double_List* Head, 
        int DataItem){
      Double_List *ptr; //вспомогательный указатель
      ptr = Head;
      while (ptr != NULL){//пока не конец списка
        if (DataItem == ptr->Data) return true; 
        else ptr = ptr->Next;
        }
      return false;
    }

    Проверка пустоты двунаправленного списка

    Операция проверки двунаправленного списка на пустоту осуществляется аналогично проверки однонаправленного списка.

    //проверка пустоты двунаправленого списка
    bool Empty_Double_List(Double_List* Head){
      if (Head!=NULL)  return false;
      else  return true;  
    }

    Удаление двунаправленного списка

    Операция удаления двунаправленного списка реализуется аналогично удалению однонаправленного списка.

    //освобождение памяти, выделенной под двунаправленный список
    void Delete_Double_List(Double_List* Head){
      if (Head != NULL){
        Delete_Double_List(Head->Next);
        delete Head;
      } 
    }

    Пример 1. N -натуральных чисел являются элементами двунаправленного списка L, вычислить: X1*Xn+X2*Xn-1+...+Xn*X1. Вывести на экран каждое произведение и итоговую сумму.

    Алгоритм:

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

    //поиск последнего элемента списка
    Double_List* Find_End_Item_Double_List(Double_List* Head){
      Double_List *ptr; //дополнительный указатель
      ptr = Head;
      while (ptr->Next != NULL){
        ptr = ptr->Next;
      }
      return ptr;
    }
    
    //итоговая сумма произведений
    void Total_Sum(Double_List* Head) {
      Double_List* lel = Head;
      Double_List* mel = Find_End_Item_Double_List(Head);
      int mltp,sum=0;
      while(lel != NULL) {
        mltp = (lel->Data)*(mel->Data);//умножение элементов 
        printf("\n\n%d * %d = %d",lel->Data,mel->Data,mltp);
        sum = sum + mltp;//суммирование произведений 
        lel = lel->Next; 
        //идем по списку из первого элемента в последний
        mel = mel->Prior; 
        //идем по списку из последнего элемента в первый
      }
      printf("\n\n Итоговая сумма равна %d",sum);
    }

    Ключевые термины

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

    Длина списка – это величина, равная числу элементов в списке.

    Линейный список – это список, отражающий отношения соседства между элементами.

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

    Пустой список – это список нулевой длины.

    Связанный список – это структура, элементами которой служат записи одного формата, связанные друг с другом с помощью указателей, хранящихся в самих элементах.

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

    Указатель начала списка (голова списка) – это указатель на первый элемент списка.

    Краткие итоги

  • Список является динамической структурой, для элементов которого определены операции включения, исключения.
  • В связанном списке элементы линейно упорядочены указателями, входящими в состав элементов списка.
  • Линейные связные списки являются простейшими динамическими структурами данных и в зависимости от организации связей делятся на однонаправленные и двунаправленные.
  • В однонаправленном (односвязном) списке каждый из элементов содержит информационную часть и указатель на следующий элемент списка. Адресное поле последнего элемента имеет значение NULL.
  • Каждый элемент списка содержит ключ, который идентифицирует этот элемент.
  • Основными операциями с однонаправленными списками, являются: создание списка; печать (просмотр) списка; вставка элемента в список; удаление элемента из списка; поиск элемента в списке; проверка пустоты списка; удаление списка.
  • В двунаправленном (двусвязном) списке каждый из элементов содержит информационную часть и два указателя на соседние элементы.
  • Основные операции, выполняемые над двунаправленным списком, те же, что и для однонаправленного списка.
  • Лабораторная работа 29. Динамические структуры данных: однонаправленные и двунаправленные списки

    Цель работы: изучить понятия, классификацию и объявления списков, особенности доступа к данным и работу с памятью при использовании однонаправленных и двунаправленных списков, научиться решать задачи с использованием списков на языке C++.

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

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

    Ознакомьтесь с материалом лекции 29.

    Задания к лабораторной работе.

    Выполните приведенные ниже задания.

  • Сформируйте однонаправленный список с вещественным информационным полем. Добавьте в список заданный элемент после первого элемента с аналогичным информационным полем. Выполните печать списка до и после изменений. Найти среднее арифметическое элементов списка.
  • Сформируйте однонаправленный список с информационным полем типа char*. Добавьте в этот список элементы с нечетными номерами. Решите задачу, выполняя следующие требования:
  • Сформировать однонаправленный список, тип информационного поля указан в варианте.
  • Распечатать полученный список.
  • Выполнить обработку списка в соответствии с заданием.
  • Распечатать полученный список.
  • Удалить список из памяти.
  • Для решения задачи сформируйте двунаправленный список с символьным информационным полем. Дана последовательность латинских букв, оканчивающаяся точкой. Среди букв есть специальный символ Ch, появление которого означает отмену предыдущего символа. Учитывая вхождение этого символа, преобразуйте последовательность.
  • Для решения задачи сформируйте двунаправленный список. Даны действительные числа a1, a2, ..., a2n (n>= 2, заранее неизвестно и вводится с клавиатуры). Вычислите: max(min(a1, a2n), min(a3, a2n-2), ... , min (a2n-1, a2)).
  • Указания к выполнению работы.

    Каждое задание необходимо решить в соответствии с изученными методами формирования, вывода и обработки данных однонаправленных или двунаправленных списков в языке С++. Обработку списков следует выполнить на основе базовых алгоритмов: поиск по списку, вставка элемента в список, удаление элемента из списка, удаление всего списка. При объявлении списков выполните комментирование используемых полей. Программу для решения каждого задания необходимо разработать методом процедурной абстракции, оформив комментарии к коду.

    Следует реализовать каждое задание в соответствии с приведенными этапами:

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

    Отчет по лабораторной работе должен соответствовать следующей структуре.

  • Титульный лист.
  • Словесная постановка задачи. В этом подразделе проводится полное описание задачи. Описывается суть задачи, анализ входящих в нее физических величин, область их допустимых значений, единицы их измерения, возможные ограничения, анализ условий при которых задача имеет решение (не имеет решения), анализ ожидаемых результатов.
  • Математическая модель. В этом подразделе вводятся математические описания физических величин и математическое описание их взаимодействий. Цель подраздела – представить решаемую задачу в математической формулировке.
  • Алгоритм решения задачи. В подразделе описывается разработка структуры алгоритма, обосновывается абстракция данных, задача разбивается на подзадачи. Схема алгоритма выполняется по ЕСПД (ГОСТ 19.003-80 и ГОСТ 19.002-80).
  • Листинг программы. Подраздел должен содержать текст программы на языке программирования С++, реализованный в среде MS Visual Studio 2010.
  • Контрольный тест. Подраздел содержит наборы исходных данных и полученные в ходе выполнения программы результаты.
  • Выводы по лабораторной работе.
  • Ответы на контрольные вопросы.
  • Контрольные вопросы

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