Кольцевой односвязный список

Последнее обновление: 25.10.2016

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

Кольцевой список в C#

Например, если у нас список состоит из одного головного элемента head, то мы можем замкнуть такой список следующим образом:

head.next = head;

Для реализации возьмем класс элемента, который используется в односвязном списке:

public class Node<T>
{
    public Node(T data)
    {
        Data = data;
    }
    public T Data { get; set; }
    public Node<T> Next { get; set; }
}

Теперь определим класс кольцевого списка:

using System.Collections;
using System.Collections.Generic;

namespace SimpleAlgorithmsApp
{
    public class CircularLinkedList<T> : IEnumerable<T>  // кольцевой связный список
    {
        Node<T> head; // головной/первый элемент
        Node<T> tail; // последний/хвостовой элемент
        int count;  // количество элементов в списке

        // добавление элемента
        public void Add(T data)
        {
            Node<T> node = new Node<T>(data);
            // если список пуст
            if (head == null)
            {
                head = node;
                tail = node;
                tail.Next = head;
            }
            else
            {
                node.Next = head;
                tail.Next = node;
                tail = node;
            }
            count++;
        }
        public bool Remove(T data)
        {
            Node<T> current = head;
            Node<T> previous = null;

            if (IsEmpty) return false;

            do
            {
                if (current.Data.Equals(data))
                {
                    // Если узел в середине или в конце
                    if (previous != null)
                    {
                        // убираем узел current, теперь previous ссылается не на current, а на current.Next
                        previous.Next = current.Next;

                        // Если узел последний,
                        // изменяем переменную tail
                        if (current == tail)
                            tail = previous;
                    }
                    else // если удаляется первый элемент
                    {
                        
                        // если в списке всего один элемент
                        if(count==1)
                        {
                            head = tail = null;
                        }
                        else
                        {
                            head = current.Next;
                            tail.Next = current.Next;
                        }
                    }
                    count--;
                    return true;
                }

                previous = current;
                current = current.Next;
            } while (current != head);

            return false;
        }

        public int Count { get { return count; } }
        public bool IsEmpty { get { return count == 0; } }

        public void Clear()
        {
            head = null;
            tail = null;
            count = 0;
        }

        public bool Contains(T data)
        {
            Node<T> current = head;
            if (current == null) return false;
            do
            {
                if (current.Data.Equals(data))
                    return true;
                current = current.Next;
            }
            while (current != head);
            return false;
        }

        IEnumerator IEnumerable.GetEnumerator()
        {
            return ((IEnumerable)this).GetEnumerator();
        }

        IEnumerator<T> IEnumerable<T>.GetEnumerator()
        {
            Node<T> current = head;
            do
            {
                if (current != null)
                {
                    yield return current.Data;
                    current = current.Next;
                }
            }
            while (current != head);
        }
    }
}

Здесь нам также потребуются ссылки на условно первый и последний элемент в виде переменных head и tail, хотя формально список будет кольцевым не будет иметь начала и конца.

Добавление фактически сводится к переустановке указателя на последний элемент tail, а новый элемент помещается между tail и head:

public void Add(T data)
{
    Node<T> node = new Node<T>(data);
    // если список пуст
    if (head == null)
    {
		head = node;
        tail = node;
        tail.Next = head;
    }
    else
    {
        node.Next = head;
		tail.Next = node;
        tail = node;
    }
    count++;
}
Добавление в кольцевой двусвязный список в C#

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

public bool Remove(T data)
{
    Node<T> current = head;
    Node<T> previous = null;

    if (IsEmpty) return false;

    do
    {
        if (current.Data.Equals(data))
        {
            // Если узел в середине или в конце
            if (previous != null)
            {
                // убираем узел current, теперь previous ссылается не на current, а на current.Next
                previous.Next = current.Next;

                // Если узел последний,
                // изменяем переменную tail
                if (current == tail)
                    tail = previous;
            }
			else // если удаляется первый элемент
			{
				// если в списке всего один элемент
				if(count==1)
				{
					head = tail = null;
				}
				else
				{
					head = current.Next;
					tail.Next = current.Next;
				}
			}
            count--;
            return true;
        }
        previous = current;
        current = current.Next;
		
    } while (current != head);

    return false;
}
Удаление из кольцевого двусвязного списка в C#

Применение кольцевого списка:

CircularLinkedList<string> circularList = new CircularLinkedList<string>();

circularList.Add("Tom");
circularList.Add("Bob");
circularList.Add("Alice");
circularList.Add("Jack");
foreach (var item in circularList)
{
    Console.WriteLine(item);
}

circularList.Remove("Bob");
Console.WriteLine("\n После удаления: \n");
foreach (var item in circularList)
{
    Console.WriteLine(item);
}

Консольный вывод:

Tom
Bob
Alice
Jack

После удаления:

Tom
Alice
Jack
Помощь сайту
Юмани:
410011174743222
Перевод на карту
Номер карты:
4048415020898850