Я пытаюсь понять функцию «поставить в очередь», которую выполнял мой профессор, но не понимаю некоторых шагов.

struct queue_node {
int item;
struct queue_node* next;
};
typedef struct queue_node* queue;


int enqueue (queue* tail, int i) {
queue n;
queue *iter;
n = (queue)malloc(sizeof(struct queue_node));
if (!n) return 1;
n->item = i;
n->next = NULL;
for (iter=tail; *iter != NULL; iter = &((*iter)->next)
;
*iter = n;
return 0;
}

Прежде всего, что "typedef struct queue_node * queue;" сбивает меня с толку, поэтому я попытался переинтерпретировать код таким образом (исправьте код, если я ошибаюсь)

struct queue_node {
int item;
struct queue_node* next;
};
typedef struct queue_node queue;

int enqueue (queue **tail, int i) {
queue *n;
queue **iter;
n = (queue)malloc(sizeof(struct queue_node));
if (!n) return 1; --->what does that mean?
n->item = i;
n->next = NULL;
for (iter=tail; **iter != NULL; iter = &((*iter)->next)--->last part of the for is unclear to me... can i rewrite it as "iter = **((iter)->next)"?
;
*iter = n; -->this is the part i don't really get...
return 0;
}

Так что, кстати, прежде чем пытаться прочитать решение моего профессора, я попытался самостоятельно выполнить функцию «поставить в очередь».

typedef struct node{
int value;
struct node *next;
}node;

void enqueue(node *head,int data){
if(head->next != NULL){
enqueue(head->next,data);
}
node *new=NULL;
new=malloc(sizeof(node));
new->value=data;
new->next=NULL;
head->next=new;
}

Это хорошо? или я не могу его использовать? Заранее всем спасибо за помощь

1
Giorgio M. 4 Сен 2016 в 17:17

2 ответа

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

Указатель tail относится к тому самому правому узлу, из которого вы должны выйти из очереди, и чтобы добраться до места, которое вы ставите в очередь, вы выполняете итерацию, пока не дойдете до начала очереди. Теперь важно отметить, что iter не указывает непосредственно на узлы, он указывает на ячейки next внутри каждого узла, пока не найдет ячейку NULL, как я пытаюсь проиллюстрировать Вот.

enter image description here

Я думаю, что на практике вы правы, не желая перебирать очередь только для добавления узла. Реальные реализации очередей (которые иногда выполняются с использованием связанных списков) требуют постоянного времени O(1) постановок в очереди и удаления из очереди, поэтому они все время держатся за указатель на любой конец очереди.

И последнее: у всех разные мнения о соглашениях об именах C, но я склонен согласиться с вами, что в примере профессора были некоторые запутанные определения типов. Некоторые кодовые базы, такие как ядро ​​Linux, рекомендуют вообще не использовать typedef для указателей или даже структур.

2
Trevor Merrifield 4 Сен 2016 в 15:48

Если использовать ваше определение queue

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

typedef struct queue_node queue;

Тогда функция будет выглядеть следующим образом.

int enqueue( queue **tail, int item ) 
{
    queue *node = ( queue * )malloc( sizeof( queue ) );
    int success = node != NULL;

    if ( success )
    {
        node->item = item;
        node->next = NULL;

        while ( *tail != NULL ) tail = &( *tail )->next;

        *tail = node;
    }

    return success;
}

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

Итак, если вы объявили очередь следующим образом

Очередь * голова = NULL;

Тогда вызов функции может выглядеть как

enqueue( &head, value );

Где value - некоторое целочисленное выражение.

Как видите, вам нужно передать заголовок queue косвенно, используя указатели. В противном случае, если не использовать указатели и передать голову непосредственно функции, тогда функция получит копию головы. Таким образом, любые изменения копии в функции не повлияют на исходную голову.

В этом заявлении

    queue *node = ( queue * )malloc( sizeof( queue ) );

Создается новый узел. Функция malloc возвращает указатель на выделенный динамически узел.

В этом заявлении

    int success = node != NULL;

Ему присваивается результат выражения node != NULL (либо 1, либо 0) в зависимости от того, был ли вызов malloc успешным или нет.

Если вызов malloc был успешным, то есть node не равно NULL, то новый узел инициализируется и добавляется в конец очереди. если (успех) { узел-> элемент = элемент; узел-> следующий = NULL;

        while ( *tail != NULL ) tail = &( *tail )->next;

        *tail = node;
    }

Как найти хвост списка?

Если список изначально был пуст, то *tail равно NULL, а условие на while *tail != NULL будет равно false. Таким образом, текущее значение tail, равное NULL, заменяется на адрес выделенного узла.

*tail = node;

В противном случае, если *tail не равно NULL, мы получаем поле данных next текущего узла

( *tail )->next

И, в свою очередь, берем его адрес так же, как мы передали голову функции по ссылке

&( *tail )->next

В конце концов, когда это поле данных содержит NULL, мы заменяем это NULL на адрес нового созданного узла.

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

typedef struct node
{
    int value;
    struct node *next;
} node;

void enqueue( node **tail, int data )
{
    if ( *tail != NULL )
    { 
        enqueue( &( *tail )->next, data );
    }
    else
    {
        node *tmp = malloc( sizeof( node ) );
        tmp->value = data;
        tmp->next = NULL;

        *tail = tmp;
    }
}
2
Vlad from Moscow 5 Сен 2016 в 08:16