美文网首页
数据结构与算法 队列

数据结构与算法 队列

作者: 今年27 | 来源:发表于2020-04-20 12:51 被阅读0次

栈是讲一种先进后出的数据结构,而在实际问题中还经常使用一种“先进先出”的数据结构:即插入在表一端进行,而删除在表的另一端进行,这叫数据结构被称为队列(Queue [kju])。允许插入的一端被称为队尾(rear),允许删除的一端成为队头(front)。如一个队列入队顺序依次为:a1;a2;a3;a4;a5,出队时顺序将依然是a1;a2;a3;a4;a5。就像超市排队的人结账。

队列也是一种运算受限的线性表,又叫先进先出表,简称“FIFO表”。

队列同样在物理存储上有两种结构

1.顺序存储的队列称为顺序队。
因为队列的队头和队尾都是活动的,因此,除了队列的数据区外还有队头、队尾两个指针。顺序队定义如下

typedef struct{
    int dataList[MAXSIZE];
    int rear;
    int font;
}SqQueue;

一些相关计算如下:

//队列的长度
int queueLength(SqQueue* queue){
    return (queue->rear - queue->font + MAXSIZE)%MAXSIZE;
}
//队列的初始化
int initQueue(SqQueue* queue){
    queue = malloc(sizeof(SqQueue));
    if (queue == NULL) {
        return 0;
    }
    queue->font = 0;
    queue->rear = 0;
    return 1;
}
//清空队列
int clearQueue(SqQueue* queue){
    queue->font = 0;
    queue->rear = 0;
    return 0;
}
//遍历队列
int traverseQueue(SqQueue* queue){
    int i = queue->font;
    while (i != queue->rear) {
        printf("%d ", queue->dataList[i]);
        i++;
    }
    printf("\n");
    return 0;
}
//队列为空的判断
int queueEmpty(SqQueue* queue){
    if (queue -> font == queue -> rear) {
        return 1;
    }
    return 0;
}
//入队
int in_queue(SqQueue* queue, int data){
    if ((queue -> rear + 1)%MAXSIZE == queue -> font) {
        return 0;
    }
    queue->dataList[queue->rear] = data;
    queue->rear = (queue->rear + 1)%MAXSIZE;
    return 1;
}
//出队
int out_queue(SqQueue* queue, int *e){
    if (queue -> font == queue -> rear) {
        return 0;
    }
    *e = queue -> dataList[queue->font];
    queue->font  = (queue -> font + 1)%MAXSIZE;
    return 1;
}

顺序存储结构由于有头和尾,在出队和入队的过程中产生假溢出的现象,


假溢出

如上图,仅仅单纯用rear = front来判断是否为空是不合理的

所以我们将之看做一个循环的队列,用来解决这种问题


循环队列原理

也就是队列为空的时候rear = front,入队: rear ++, front不变。出队:front++, rear不变。因为是循环队列,就要考虑MAXSIZE的取余运算,以达到合理运算结果,所以队列满的话:(rear + 1)%MAXSIZE= front。循环队列的入队则是(++rear)%MAXSIZE,同样出队则是(++front)%MAXSIZE

好处:
降低了出队时所耗费的时间复杂度,因为队列先进先出的原则,导致在dequeue(出栈操作)时,必须删除队首的元素,后面所有的元素都要往前移动一位,导致时间复杂度为O(n)。如果弄成循环队列,引入front(队首)、rear(队尾)指针,则出队时,后面所有的元素不用动,只要front+1,时间复杂度为O(1); 另一方面:节约了空间,因为当队前面的元素删除时,tail指向队尾后,还可以再从第一个元素循环。 补充:队列为空时,front = rear 队列满时,(rear+1)% length = front 解释: 因为rear+1 <= length,当rear+1 =length时,说明tail在队尾,余 数为0,front为0,队列已满;当rear+1<length时,余数为tail + 1,则rear+1+front = length,队也已满。 注意:循环队列满的时候,rear指向的位置是空的,说明length-1才是实际元素的个数,会有一个空位没有元素

2.链式存储的队列称为链队列。
和链栈类似,链队列可以用单链表来实现,根据队的FIFO原则,为了操作上的方便,可以分别设置一个头指针和一个尾指针。
链式存储相对来说比顺序存储相对来说可以无限链,对于MAXSIZE的考量,仁者见仁智者见智。我这里不做限制。


链式队列
typedef struct Node{
    struct Node* next;
    int data;
}Node;

typedef Node* QueuePtr;

typedef struct {
    QueuePtr rear;
    QueuePtr front;
}LinkQueue;

/// 初始化队列
/// @param queue 队列
int initQueue(LinkQueue* queue){
//    queue = malloc(sizeof(LinkQueue));
//    if (queue == NULL) {
//        return 0;
//    }
    
    Node* fristNode = malloc(sizeof(Node));
    
    if (fristNode == NULL) {
        return 0;
    }
    
    queue->rear = queue -> front = fristNode;

    queue->front->next = NULL;
    return 1;
}

/// 入队
/// @param queue 队列
/// @param data 数据
int in_queue(LinkQueue* queue, int data){
    Node* node = malloc(sizeof(node));
    if (node == NULL) {
        return 0;
    }
    node->data = data;
    node->next = NULL;
//    queue->rear = node;
    queue->rear->next = node;
    queue->rear = node;//修改队尾指针
    
    return 1;
}
//判断队列为空
int queueEmpty(LinkQueue queue){
    return queue.front == queue.rear;
}

/// 清空队列
/// @param queue 队列
int clear_queue(LinkQueue* queue){
    Node* p = queue -> front ->next;
    Node* tem = NULL;
    while (p) {
        tem = p -> next;
        free(p);
        p = tem;
    }
    queue -> front = queue -> rear;
    queue->front->next = NULL;
    return 1;
}

/// 出队
/// @param queue 队列
/// @param e 记录出队的元素
int out_queue(LinkQueue* queue, int *e){
    if (queue->front == queue->rear) {
        return 0;
    }
    Node* tem = queue->front->next;//拿到队的首的元素
    *e = tem->data;
    queue->front->next = tem->next;//将队头的next指向首元素的下一个元素,即第二个元素
    if (queue->rear == tem) {//即里面只有一个元素了,这个元素取完队尾与队首一致
        queue->rear = queue->front;
    }
    free(tem);
    return 1;
}

/// 队长度
/// @param queue 队列
int queue_length(LinkQueue queue){
    QueuePtr p = queue.front;
    int i = 0;
    while (queue.rear != p) { //不能简单的用p != null来l判断,至于为什么自己想
        i++;
        p = p->next;
    }
    return i;
    
}

/// 销毁队列
/// @param queue 队列
int destoryQueue(LinkQueue* queue){
    Node * p = queue->front;
    Node * tmp = NULL;
    while (p) {
        tmp = p ->next;
        free(p);
        p = tmp;
    }
    queue -> front = queue->rear;
    return 1;
}

/// 遍历队
/// @param queue 队列
int traverseQueue(LinkQueue* queue){
    if (queue == NULL) {
        return 0;
    }
    Node* p = queue->front->next;
//    Node* tem = NULL;
    while (p) {
        printf("%d ", p->data);
        p = p -> next;
    }
    printf("\n");
    return 1;
}

相关文章

  • 数据结构与算法 (队列实现篇)

    数据结构与算法 (队列实现篇) 在数据结构与算法中,队列(queue)是一种受限的线性储存结构,特殊之处在于它只允...

  • 排序算法

    什么是算法 书籍推荐 《数据结构与算法分析》 表、栈和队列 树 散列(hash) 优先队列(堆) 排序 定义 问题...

  • 集合相关数据结构与算法

    队列 栈数据结构 比较算法 Collections Collection与Collections的区别?Colle...

  • 数据结构与算法学习开篇

    数据结构与算法知识图谱 20个最常用的、最基础数据结构与算法 10个数据结构:数组、链表、栈、队列、散列表、二叉树...

  • 【数据结构与算法 - Swift实现】10 - 优先队列 (Pr

    在【数据结构与算法 - Swift实现】04 - 队列 (Queue)文章里面,我们已经讲过队列,采用先进先出的规...

  • 数据结构与算法分析:大纲]

    00数据结构与算法分析:大纲01数据结构:数组02数据结构:链表03数据结构:栈03数据结构:队列 本系列课程主要...

  • 数据结构:数组

    00数据结构与算法分析:大纲01数据结构:数组02数据结构:链表03数据结构:栈03数据结构:队列 数组 数组是一...

  • 链式队列的定义,ios基础知识篇!

    数据结构与算法-链式队列 链式队列的定义 链式队列是用链表来实现的队列,不存在队满的情况。链式队列也包里队列的特点...

  • 数据结构与算法之美-09讲队列

    数据结构与算法之美-09讲队列 特别备注 本系列非原创,文章原文摘自极客时间-数据结构算法之美[https://t...

  • 队列

    数据结构与算法 --- 队列 【转载】 原文:https://www.jianshu.com/p/c8f2524e...

网友评论

      本文标题:数据结构与算法 队列

      本文链接:https://www.haomeiwen.com/subject/vivrihtx.html