队列通常与堆栈一起教授,抽象数据类型根据我们在队列中执行的操作来定义。堆栈和队列之间的最大区别在于,它们执行的操作顺序,队列是先进先出,即先进先出,这意味着,首先进入队列的东西首先出去,而堆栈是最后进入的先出,所以我们最后放入的就是我们将返回的第一个
队列是根据三个操作定义的:
我们可以把队列想象成餐厅里的队伍,当我们排队时,我们必须等待前面的每个人都在我们的时间之前得到服务。
这是使用数组的队列的简单实现:
#include#include #include #define QUEUE_LEN 1024 struct queue_t { uint32_t data[QUEUE_LEN]; size_t ptr; }; void enqueue(struct queue_t *queue, uint32_t value) { if (queue->ptr 1 >= QUEUE_LEN) { fprintf(stderr, "The queue is full"); exit(1); } queue->data[queue->ptr ] = value; } uint32_t dequeue(struct queue_t *queue) { if (queue->ptr == 0) { fprintf(stderr, "Cannot dequeue empty queue"); exit(1); } uint32_t val = queue->data[0]; for (size_t i = 1; i ptr; i) { queue->data[i - 1] = queue->data[i]; } queue->ptr--; return val; } uint32_t peek(struct queue_t *queue) { if (queue->ptr == 0) { fprintf(stderr, "Cannot peek empty queue"); exit(1); } return queue->data[0]; }
这里有一个有趣的实现细节,每当我们出队时,因为我们要从队列前面删除一个元素,
我们必须将以下所有元素移回原处,因此在这个实现中,队列的复杂度为 O(n),为了避免这种情况,我们需要有一个 LinkedList 作为底层数据结构,通过这样做,我们可以只移动指向下一个的头指针,而不必执行所有这些操作。
#include#include #include struct node_t { uint32_t data; struct node_t *next; }; struct linked_list_t { struct node_t *head; struct node_t *tail; size_t len; }; void enqueue(struct linked_list_t *list, uint32_t data) { struct node_t *node = malloc(sizeof(struct node_t)); node->data = data; node->next = NULL; list->len ; if (list->len == 1) { list->head = list->tail = node; return; } list->tail->next = node; list->tail = node; } uint32_t dequeue(struct linked_list_t *list) { if (list->len == 0) { fprintf(stderr, "Cannot dequeue empty list"); exit(1); } struct node_t *aux = list->head; uint32_t data = aux->data; list->head = list->head->next; list->len--; free(aux); return data; } uint32_t peek(struct linked_list_t *list) { if (list->len == 0) { fprintf(stderr, "Cannot peek empty list"); exit(1); } return list->head->data; } void list_free(struct linked_list_t *list) { struct node_t *prev = NULL; struct node_t *aux = list->head; while (aux != NULL) { prev = aux; aux = aux->next; if (prev) { free(prev); } } }
这里可以看到,入队和出队时都没有迭代,我们只是调整指针,所以这就是为什么这个实现在出队时有更好的时间复杂度。
不过,有一个小警告,由于缓存局部性,即使这种实现在最坏的情况下速度更快,但在大多数情况下可能不是这样。
免责声明: 提供的所有资源部分来自互联网,如果有侵犯您的版权或其他权益,请说明详细缘由并提供版权或权益证明然后发到邮箱:[email protected] 我们会第一时间内为您处理。
Copyright© 2022 湘ICP备2022001581号-3