Учебники

2) Круговой связанный список

Что такое круговой связанный список?

Круговой связанный список — это последовательность узлов, расположенных таким образом, что каждый узел может быть восстановлен сам по себе. Здесь «узел» — это самоссылающийся элемент с указателями на один или два узла в непосредственной близости от него.

Ниже приведено описание круглого связного списка с 3 узлами.

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

Примечание. Самый простой круговой связанный список — это узел, который отслеживает только себя, как показано

Из этого урока вы узнаете:

Основные операции

Основные операции над циклически связанным списком:

  1. вставка
  2. Удаление и
  3. пересечение
  • Вставка — это процесс размещения узла в указанной позиции в круговом связанном списке.
  • Удаление — это процесс удаления существующего узла из связанного списка. Узел может быть идентифицирован по вхождению его значения или по его положению.
  • Обход кругового связанного списка — это процесс отображения содержимого всего связанного списка и возврата к исходному узлу.

В следующем разделе вы поймете вставку узла и возможные типы вставки в круговой односвязный список.

Операция вставки

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

Далее есть две возможности:

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

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

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

ПРИМЕЧАНИЕ. Указатель, являющийся мастером токена или началом / концом круга, можно изменить. Он все еще вернется к тому же узлу при обходе, который будет обсуждаться заранее.

Шаги в (a) i-iii показаны ниже:

(Существующий узел)

ШАГ 1) Разорвать существующую ссылку

ШАГ 2) Создать прямую ссылку (от нового узла к существующему узлу)

ШАГ 3) Создайте петлевую ссылку на первый узел

Далее вы попробуете вставить после узла.

Например, давайте вставим «VALUE2» после узла с «VALUE0». Предположим, что отправной точкой является узел с «VALUE0».

  • Вам нужно будет разорвать линию между первым и вторым узлом и поместить узел с «VALUE2» между ними.
  • Следующий указатель первого узла должен ссылаться на этот узел, а следующий указатель этого узла должен ссылаться на более ранний второй узел.
  • Остальная часть договоренности остается без изменений. Все узлы являются прослеживаемыми для себя.

ПРИМЕЧАНИЕ. Поскольку существует циклическое расположение, вставка узла включает одну и ту же процедуру для любого узла. Указатель, завершающий цикл, завершает цикл, как и любой другой узел.

Это показано ниже:

(Let us say there are only two nodes. This is a trivial case)

STEP 1) Remove the inner link between the connected nodes

STEP 2) Connect the left-hand side node to the new node

STEP 3) Connect the new node to the right hand side node.

Deletion Operation

Let us assume a 3-node circular linked list. The cases for deletion are given below:

  • Deleting the current element
  • Deletion after an element.

Deletion at the beginning/end:

  1. Traverse to the first node from the last node.
  2. To delete from the end, there should be only one traversal step, from the last node to the first node.
  3. Delete the link between the last node and the next node.
  4. Link the last node to the next element of the first node.
  5. Free the first node.

(Existing setup)

STEP 1) Remove the circular link

ШАГИ 2) Удалить связь между первым и следующим, связать последний узел, с узлом, следующим за первым

ШАГ 3) Освободить / освободить первый узел

Удаление после узла:

  1. Пройдите до следующего узла, который будет удален.
  2. Перейдите к следующему узлу, поместив указатель на предыдущий узел.
  3. Подключите предыдущий узел к узлу после текущего узла, используя его следующий указатель.
  4. Освободить текущий (разделенный) узел.

ШАГ 1) Допустим, нам нужно удалить узел с «VALUE1».

ШАГ 2) Удалить связь между предыдущим узлом и текущим узлом. Свяжите свой предыдущий узел со следующим узлом, указанным текущим узлом (с VALUE1).

ШАГ 3) Освободить или освободить текущий узел.

Обход кругового связного списка

Чтобы просмотреть круговой связанный список, начиная с последнего указателя, проверьте, не равен ли последний указатель NULL. Если это условие ложно, проверьте, существует ли только один элемент. В противном случае переходите с использованием временного указателя, пока последний указатель не будет достигнут снова или столько раз, сколько необходимо, как показано в GIF ниже.

Преимущества кругового связного списка

Некоторые из преимуществ круговых связанных списков:

  1. Нет требования для присвоения NULL в коде. Круговой список никогда не указывает на нулевой указатель, если он полностью не освобожден.
  2. Круговые связанные списки выгодны для конечных операций, так как начало и конец совпадают. Алгоритмы, такие как планирование Round Robin, могут аккуратно устранять процессы, которые ставятся в очередь по кругу, не встречая висячих или NULL-ссылочных указателей.
  3. Круговой связанный список также выполняет все обычные функции односвязного списка. Фактически, обсуждаемые ниже круговые двусвязные списки могут даже устранить необходимость в полномасштабном обходе для определения местоположения элемента. Этот элемент в лучшем случае будет точно противоположным началу, завершив только половину связанного списка.

Единственный связанный список как круговой связанный список

Вам предлагается попытаться прочитать и реализовать приведенный ниже код. Он представляет арифметику указателя, связанную с круговыми связными списками.

#include<stdio.h>
#include<stdlib.h>

struct node
{
    int item;
    struct node *next;
};

struct node* addToEmpty(struct node*,int);
struct node *insertCurrent(struct node *, int);
struct node *insertAfter(struct node *, int, int);
struct node *removeAfter(struct node *, int);
struct node *removeCurrent(struct node *);

void peek(struct node *);

int main()
{
...

Объяснение кода:

  1. Первые две строки кода являются необходимыми заголовочными файлами.
  2. В следующем разделе описывается структура каждого самоссылающегося узла. Он содержит значение и указатель того же типа, что и структура.
  3. Каждая структура неоднократно ссылается на объекты структуры одного типа.
  4. Существуют разные прототипы функций для:
    1. Добавление элемента в пустой связанный список
    2. Вставка в текущей указанной позиции кругового связанного списка.
    3. Вставка после определенного индексированного значения в связанный список.
    4. Удаление / Удаление после определенного индексированного значения в связанном списке.
    5. Удаление в текущей указанной позиции кругового связанного списка
  5. Последняя функция печатает каждый элемент посредством кругового обхода в любом состоянии связанного списка.
int main()
{
    struct node *last = NULL;
    last = insertCurrent(last,4);
    last = removeAfter(last, 4);
    peek(last);
    return 0;
}

struct node* addToEmpty(struct node*last, int data)
{
    struct node *temp = (struct node *)malloc(sizeof( struct node));
    temp->item = data;
    last = temp;
    last->next = last;
    return last;
}
    
struct node *insertCurrent(struct node *last, int data)

Объяснение кода:

  1. Для кода addEmpty выделите пустой узел с помощью функции malloc.
  2. Для этого узла поместите данные в переменную temp.
  3. Назначьте и свяжите единственную переменную с переменной temp
  4. Вернуть последний элемент в контекст main () / application.
struct node *insertCurrent(struct node *last, int data)
{
    if(last == NULL)
    {
       return    addToEmpty(last, data);
    }
    struct node *temp = (struct node *)malloc(sizeof( struct node));
    temp -> item = data;
    temp->next = last->next;
    last->next = temp;
    return last;
}
struct node *insertAfter(struct node *last, int data, int item)
{
    struct node *temp = last->next, *prev = temp, *newnode =NULL;
…

Объяснение кода

  1. Если нет элемента для вставки, то вы должны обязательно добавить в пустой список и вернуть управление.
  2. Создайте временный элемент для размещения после текущего элемента.
  3. Свяжите указатели, как показано.
  4. Вернуть последний указатель, как в предыдущей функции.
...
struct node *insertAfter(struct node *last, int data, int item)
{
    struct node *temp = last->next, *prev = temp, *newnode =NULL;
    if (last == NULL)
    {
       return addToEmpty(last, item);
    }
    do
    {
        prev = temp;
        temp = temp->next;
    } while (temp->next != last && temp->item != data );


    if(temp->item != data)
    {
       printf("Element not found. Please try again");
...

Объяснение кода:

  1. Если в списке нет элемента, игнорируйте данные, добавьте текущий элемент в качестве последнего элемента в списке и верните элемент управления
  2. Для каждой итерации в цикле do-while убедитесь, что есть предыдущий указатель, который содержит последний пройденный результат.
  3. Только тогда может произойти следующий обход.
  4. Если данные найдены, или температура достигает последнего указателя, выполнение завершается. Следующий раздел кода решает, что должно быть сделано с элементом.
...
    if(temp->item != data)
    {
       printf("Element not found. Please try again");
       return last;
    }
    else
    {
   	 newnode = (struct node *)malloc(sizeof(struct node));
             newnode->item = item;
             prev->next = newnode;
             newnode->next = temp;
    }
    return last;
}

struct node *removeCurrent(struct node *last)
...

Объяснение кода:

  1. Если весь список был пройден, но элемент не найден, отобразите сообщение «элемент не найден», а затем верните управление вызывающей стороне.
  2. Если найден узел, и / или узел еще не последний, то создайте новый узел.
  3. Свяжите предыдущий узел с новым узлом. Свяжите текущий узел с temp (переменная обхода).
  4. Это гарантирует, что элемент будет размещен после определенного узла в круговом связанном списке. Вернитесь к звонящему.
struct node *removeCurrent(struct node *last)
{
    if(last == NULL)
    {
        printf("Element Not Found");
        return NULL;
    }
    struct node *temp = last->next;
    last->next = temp->next;
    free(temp);
    return last;
}

struct node *removeAfter(struct node *last, int data)

Объяснение кода

  1. Чтобы удалить только последний (текущий) узел, убедитесь, что этот список пуст. Если это так, то ни один элемент не может быть удален.
  2. Переменная temp просто пересекает одну ссылку вперед.
  3. Свяжите последний указатель с указателем после первого.
  4. Освободите временный указатель. Он освобождает несвязанный последний указатель.
struct node *removeAfter(struct node *last,int data)
{
    struct node *temp = NULL,*prev = NULL;
    if (last == NULL)
    {
   	 printf("Linked list empty. Cannot remove any element\n");
   	 return NULL;
    }
    temp = last->next;
    prev = temp;
    do
    {
        prev = temp;
        temp = temp->next;
    } while (temp->next != last && temp->item != data );

    if(temp->item != data)
    {
      printf("Element not found");
...

Объяснение кода

  1. Как и в предыдущей функции удаления, проверьте, нет ли элемента. Если это правда, то ни один элемент не может быть удален.
  2. Указателям назначаются определенные позиции для определения местоположения удаляемого элемента.
  3. Указатели продвинуты, один за другим. (предыдущий темп)
  4. Процесс продолжается до тех пор, пока элемент не будет найден или пока следующий элемент не вернется к последнему указателю.
    if(temp->item != data)
    {
        printf("Element not found");
        return last;
    }
    else
    {
        prev->next = temp->next;
        free(temp);
    }
    return last;
}

void peek(struct node * last)
{
    struct node *temp = last;
    if (last == NULL)
    {
   return;	

Объяснение программы

  1. Если элемент найден после обхода всего связанного списка, отображается сообщение об ошибке, в котором говорится, что элемент не найден.
  2. В противном случае элемент разделяется и освобождается на шагах 3 и 4.
  3. Предыдущий указатель связан с адресом, указанным как «следующий» удаляемым элементом (temp).
  4. Поэтому временный указатель освобождается и освобождается.
...
void peek(struct node * last)
{
    struct node *temp = last;
    if (last == NULL)
    {
         return;    
    }
    if(last -> next == last)
    {
        printf("%d-", temp->item);
    }
    while (temp != last)
    {
       printf("%d-", temp->item);
       temp = temp->next;
    }
}

Объяснение кода

  1. Просмотр или обход невозможны, если нужны нули. Пользователь должен выделить или вставить узел.
  2. Если есть только один узел, нет необходимости проходить через него, содержимое узла может быть напечатано, и цикл while не выполняется.
  3. Если существует более одного узла, то temp печатает весь элемент до последнего элемента.
  4. Момент времени достигается последний элемент, цикл завершается, и функция возвращает вызов основной функции.

Приложения Циркулярного связного списка

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