Странность очереди / очереди? - PullRequest
1 голос
/ 27 января 2012

Я работал над заданием, включающим реализацию очередей, содержащих пустые указатели, чтобы их можно было обобщать для любого типа данных.Я в настоящее время сталкиваюсь со странной проблемой, хотя, где удаление узлов уменьшает размер списка, но не возвращает ожидаемые узлы.Пропуск вызова free () в операции удаления исправляет это, но, поскольку я хочу освободить узлы, находящиеся в очереди, это нежелательно.Любые советы?

Тестовый прогон: рутина.c

#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include "queue.h"

int main() {
  queue test = make_queue();
  enqueue("One", test);
  enqueue("Two", test);
  printf("Item is %s!\n", (char *)dequeue(test));
  printf("Item is %s!\n", (char *)dequeue(test));
  return 0;
}

queue.h

#include <stdbool.h>
#include <stdlib.h>
#include <stdio.h>
/* A queue is implemented as a pointer to a structure not specified here. */

typedef struct queue_structure *queue;

struct node {
  struct node * next;
  void * data;
};

struct queue_structure {
  struct node * head;
  struct node * tail;
};

/* List of function protocols. */
bool is_empty_queue(queue q);

/* The make_queue function returns a newly created queue with no values
   stored in it.
*/

queue make_queue() {
  queue newQueue = malloc(sizeof(struct queue_structure));
  return newQueue;
}

/* The enqueue function adds a value to a queue.  Although this function
   does not change the pointer q, fields of the structure to which q
   points may be modified in the course of a call to this function.
*/

void enqueue(void *value, queue q) {
  struct node * newNode = (struct node *)malloc(sizeof(struct node));
  newNode->data = value;    
  if(is_empty_queue(q))
    q->tail = newNode;
  newNode->next = q->head;
  q->head = newNode;
}

/* The dequeue function removes a value from a queue and returns it.
   Although this function does not change the pointer q, fields of the
   structure to which q points may be modified in the course of a call to
   this function.

   It is a precondition of this function that at least one value is stored
   in the queue.
*/

void *dequeue(queue q) {
  if(!q->head->next) { // Only a single item in the queue.
    printf("Only one item in queue!\n");
    struct node * to_dequeue = q->tail;
    void * data = q->head->data;
    free(to_dequeue);
    q->head = NULL;
    q->tail = NULL;
    return data;
  }
  else { // Multiple items in the queue.
    printf("Several items in queue!\n");
    struct node * to_dequeue = q->tail;
    void * data = q->tail->data;
    struct node * trace = q->head;
    while(trace->next && trace->next != q->tail)
      trace = trace->next;
    free(to_dequeue);
    q->tail = trace;
    q->tail->next = NULL;
    return data;
  }
}

/* The front_of_queue function returns the value at the front of a queue
   (that is, the one least recently added to the queue) without removing
   that value from the queue.  It has no side effect.

   It is a precondition of this function that at least one value is stored
   in the queue.
*/

void *front_of_queue(queue q) {
  return q->head->data;
}

/* The is_empty_queue function determines whether a queue is empty,
   returning the true Boolean value if no values are stored in the queue
   and the false Boolean value if one or more values are stored in the
   queue.
*/

bool is_empty_queue(queue q) {
  if(q->head)
    return 1;
  return 0;
}

1 Ответ

1 голос
/ 27 января 2012

Вы не инициализируете head и tail до NULL в make_queue, и у вас неверный тест на пустоту,

bool is_empty_queue(queue q) {
  if(q->head)
    return 1;
  return 0;
}

, что заставляет enqueue вести себя странно.

void enqueue(void *value, queue q) {
  struct node * newNode = (struct node *)malloc(sizeof(struct node));
  newNode->data = value;    
  if(is_empty_queue(q))
    q->tail = newNode;
  newNode->next = q->head;
  q->head = newNode;
}

Случай 1, случайность head и tail равны NULL изначально

head -> 0; tail -> 0  // now enqueue 1
is_empty_queue(q) returns 0 since q->head == NULL, so q->tail still points to 0
n(1)->next = 0
head = n(1)

results in
head -> n(1) -> 0; tail -> 0  // next enqueue 2
is_empty_queue(q) returns 1 since q->head = n(1) != 0, so
q->tail = n(2)
n(2)->next = n(1)
q->head = n(2)

result:
head -> n(2) -> n(1) -> 0; tail -> n(2)

и все дальнейшие enqueue операции уйдут head == tail. Но если вы сейчас dequeue:

struct node * to_dequeue = q->tail;   // n(2)
void * data = q->tail->data;
struct node * trace = q->head;        // n(2)
while(trace->next && trace->next != q->tail)  // n(2) -> n(1) -> 0
  trace = trace->next;                // trace = n(1)
free(to_dequeue);                     // free n(2)
q->tail = trace;                      // tail -> n(1)
q->tail->next = NULL;                 // already had that

и head - висячий указатель.

Случай 2, случайность head изначально не NULL.

head -> x; tail -> y // enqueue 1
is_empty_queue(q) returns 1 because q->head == x != 0
q->tail = n(1)
n(1)->next = x
q->head = n(1)

head -> n(1) -> x; tail -> n(1) // now enqueue 2
is_empty_queue(q) returns 1 because q->head == n(1)
q->tail = n(2)
n(2)->next = n(1)
q->head = n(2)

head -> n(2) -> n(1) -> x; tail -> n(2)

Единственное отличие состоит в том, что теперь n(1)->next != 0, тогда, если вы удаляете из очереди, trace будет установлен в дикий 'указатель' x, а затем проверяется x->next, но поскольку x был неопределенным битом шаблон, который часто вызывает segfault.

Если я что-то упустил, инициализация head и tail на конструкции, исправление is_empty_queue и проверка на пустоту на dequeue даст вам рабочую программу.

Но если очередь длинная, операция удаления медленная, так как она должна пройти всю очередь, чтобы найти предпоследний элемент для обновления tail. Вы можете выполнять операции enqueue и dequeue, O (1), если ставите в очередь в позиции tail и dequeue с head.

...