Кольцо из двусвязного списка

Теги: Циклический список, замкнутый список, кольцевой список, кольцо.



Кольцо на двусвязном списке.

Если направить указатель последнего элемента двунаправленного списка не на NULL, а не первый элемент, то получим структуру-кольцо. Кольцо – циклическая структура, которая обычно реализует операции вставки и удаления, а также операции next и prev, позволяющие двигаться вперёд и назад по кольцу. Если кольцо собрано из односвязного списка, то операция prev, соответственно, не поддерживается. Наша структура-кольцо будет, подобно двусвязному списку, хранить указатель на узел кольца и его размер.

Кольцевой двусвязный список
Кольцевой двусвязный список
typedef int T;

typedef struct _Node {
	T data;
	struct _Node *next;
	struct _Node *prev;
} Node;

typedef struct _Ring {
	size_t size;
	Node *current;
} Ring;

Ring* createRing() {
	Ring *tmp = (Ring*)malloc(sizeof(Ring));
	
	tmp->size = 0;
	tmp->current = NULL;

	return tmp;
}

Никаких особенностей здесь нет. Всё очень похоже на двусвязный список. Функции добавления и удаления элементов:

void addElement(Ring *ring, T value) {
	Node *prev = NULL;
	Node *tmp = (Node*)malloc(sizeof(Node));

	tmp->data = value;
	//Если в кольце нет элементов
	if (ring->current == NULL) {
		ring->current = tmp;
		tmp->next = tmp->prev = tmp;			
	} else {
		prev = ring->current->next->prev;
		tmp->next = ring->current->next;
		tmp->prev = ring->current;
		prev->prev = tmp;
		ring->current->next = tmp;
		ring->current = tmp;	
	}
	ring->size++;
}

T removeElement(Ring *ring) {
	Node *afterTarget = NULL;
	T retVal;

	if (ring->current == NULL) {
		exit(1);
	}

	//Если в кольце последний элемент
	if (ring->current->next == ring->current) {
		retVal = ring->current->data;
		free(ring->current);
		ring->current = NULL;			
	} else {
		afterTarget = ring->current->next;
		ring->current->prev->next = afterTarget;
		afterTarget->prev = ring->current->prev;
		retVal = ring->current->data;
		free(ring->current);
		ring->current = afterTarget;
	}
	ring->size--;
	return retVal;
}

Заметьте, что при добавлении нового элемента указатель на элемент кольца сдвигается на вновь добавленный элемент. При удалении указатель current сдвигается на следующий элемент, после удаляемого. Функции prev и next позволяют двигаться по кольцевому списку. next сдвигает указатель current вперёд, prev назад.

Node* next(Ring *ring) {
	Node* retVal = NULL;
	if (ring->current) {
		ring->current = ring->current->next;
		retVal = ring->current;
	} 
	return retVal;
}

Node* prev(Ring *ring) {
	Node* retVal = NULL;
	if (ring->current) {
		ring->current = ring->current->prev;
		retVal = ring->current;
	}
	return retVal;
}

Последняя функция – circle. Она получает кольцо и функцию, которую применяет к каждому узлу кольца. Мы обходим кольцо до тех пор, пока не наткнёмся на первый узел. Все узлы имеют разный адрес в памяти благодаря тому, что функция addElement создаёт для каждого элемента новый узел

void circle(const Ring *ring, void (*f)(Node* node)) {
	Node *anchor = ring->current;
	if (anchor) {		
		do {
			f(anchor);
			anchor = anchor->next;
		} while (anchor != ring->current);
	}
}

void printNode(Node *node) {
	printf("%d ", node->data);
}
Q&A

Всё ещё не понятно? – пиши вопросы на ящик email
Базовые операции. Сортировка вставками