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

Есть ли способ удалить элемент из односвязного списка за О(1)? При условии, что у нас есть указатель на удаляемый элемент.


Ответы (1 шт):

Автор решения: Stanislav Volodarskiy

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

// gcc -std=c11 -pedantic -Wall -Wextra -Werror -O3 carmack-list.c

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

typedef struct node_t node_t;
struct node_t {
    char value;
    node_t *next;
};

void insert_head(node_t **head, char value) {
    node_t *new_node = malloc(sizeof(node_t));
    new_node->value = value;
    new_node->next = *head;
    *head = new_node;
}

void remove_head(node_t **head) {
    node_t *old_node = *head;
    *head = (*head)->next;
    free(old_node);
}

node_t **find(node_t **head, char value) {
    node_t **node;
    for (node = head; *node != NULL; node = &(*node)->next) {
        if ((*node)->value == value) {
            break;
        }
    }
    return node;
}

node_t *make_list(const char *s) {
    node_t *head = NULL;
    for (int i = strlen(s) - 1; i >= 0; --i) {
        insert_head(&head, s[i]);
    }
    return head;
}

void print_list(node_t *head) {
    for (node_t *node = head; node != NULL; node = node->next) {
        putc(node->value, stdout);
    }
    putc('\n', stdout);
}

int main() {
    node_t *list = make_list("C+-C+-C+-C");
    print_list(list);
    for (; ; ) {
        node_t **node = find(&list, 'C');
        if (*node == NULL) {
            break;
        }
        remove_head(node);
        print_list(list);
    }
}
$ gcc -std=c11 -pedantic -Wall -Wextra -Werror -O3 carmack-list.c && ./a.out 
C+-C+-C+-C
+-C+-C+-C
+-+-C+-C
+-+-+-C
+-+-+-
→ Ссылка