【队列】队列(queue)原理


像栈一样,队列(queue)也是一种线性表,它的特性是先进先出,插入在一端,删除在另一端。就像排队一样,刚来的人入队(push)要排在队尾(rear),每次出队(pop)的都是队首(front)的人。如图1,描述了一个队列模型。

 

队列(Queue)与栈一样,是一种线性存储结构,它具有如下特点:

  1. 队列中的数据元素遵循“先进先出”(First In First Out)的原则,简称FIFO结构。
  2. 在队尾添加元素,在队头删除元素。
 

1.2 队列的相关概念

队列的相关概念:

  1. 队头与队尾: 允许元素插入的一端称为队尾,允许元素删除的一端称为队头。
  2. 入队:队列的插入操作。
  3. 出队:队列的删除操作。

例如我们有一个存储整型元素的队列,我们依次入队:{1,2,3}

添加元素时,元素只能从队尾一端进入队列,也即是2只能跟在1后面,3只能跟在2后面。
如果队列中的元素要出队:

元素只能从队首出队列,出队列的顺序为:1、2、3,与入队时的顺序一致,这就是所谓的“先进先出”。

1.3 队列的操作

队列通常提供的操作:

  1. 入队: 通常命名为push()
  2. 出队: 通常命名为pop()
  3. 求队列中元素个数
  4. 判断队列是否为空
  5. 获取队首元素

1.4 队列的存储结构

队列与栈一样是一种线性结构,因此以常见的线性表如数组、链表作为底层的数据结构。
本文中,我们以数组、链表为底层数据结构构建队列。

 

2.基于数组的循环队列实现

以数组作为底层数据结构时,一般讲队列实现为循环队列。这是因为队列在顺序存储上的不足:每次从数组头部删除元素(出队)后,需要将头部以后的所有元素往前移动一个位置,这是一个时间复杂度为O(n)的操作:

可能有人说,把队首标志往后移动不就不用移动元素了吗?的确,但那样会造成数组空间的“流失”。
我们希望队列的插入与删除操作都是O(1)的时间复杂度,同时不会造成数组空间的浪费,我们应该使用循环队列。
所谓的循环队列,可以把数组看出一个首尾相连的圆环,删除元素时将队首标志往后移动,添加元素时若数组尾部已经没有空间,则考虑数组头部的空间是否空闲,如果是,则在数组头部进行插入。

 

C语言实现:数组实现的队列,并且只能存储int数据

 

C语言数组实现队列一(数组模拟):

 

#include <stdio.h>

/**
 * C 语言: 数组实现的队列,只能存储int数据。
 *
 * @author llz
 * @date 2018/7/02
 */

// 保存数据的数组
int arr[500];
// 队列的实际大小
int count;


// 将val添加到队列的末尾
void add(int val) 
{
    arr[count++] = val;
}

// 返回“队列开头元素”
int front() 
{
    return arr[0];
}

// 返回并删除“队列开头元素”
int pop() 
{
    int i = 0;;
    int ret = arr[0];
    count--;
    while (i++<count)
        arr[i-1] = arr[i];
    return ret;
}

// 返回“队列”的大小
int size() 
{
    return count;
}

// 返回“队列”是否为空
int is_empty()
{
    return count==0;
}

int main() 
{
    int tmp=0,i;
    
    // 打印队列
    for(i=0;i<size();i++)printf("%d ",arr[i]);
    printf("\n");
    
    // 将10, 20, 30 依次推入队列中
    add(10);
    add(20);
    add(30);
    
    for(i=0;i<size();i++)printf("%d ",arr[i]);
    printf("\n");

    // 将“队列开头的元素”赋值给tmp,并删除“该元素”
    tmp = pop();
    printf("tmp=%d\n", tmp);
    

    // 只将“队列开头的元素”赋值给tmp,不删除该元素.
    tmp = front();
    printf("tmp=%d\n", tmp);

    add(40);

    // 打印队列
    printf("is_empty()=%d\n", is_empty());
    printf("size()=%d\n", size());
    while (!is_empty())
    {
        printf("%d\n", pop());
    }
    return 0;

}

 

结果说明:该示例中的队列,是通过"数组"来实现的!
由于代码中已经给出了详细了注释,这里就不再对函数进行说明了。仅对主函数main的逻辑进行简单介绍。
(01) 在主函数main中,先将 "10, 20, 30"依次入队列。此时,队列的数据是: 10 --> 20 --> 30 
(02) 接着通过pop()返回队首元素;pop()操作并不会改变队列中的数据。此时,队列的数据依然是: 10 --> 20 --> 30 
(03) 接着通过front()返回并删除队首元素。front()操作之后,队列的数据是: 10 --> 30 
(04) 接着通过add(40)将40入队列。add(40)操作之后,队列中的数据是: 10 --> 20 --> 40

 

C语言数组实现队列二(数组模拟+队头队尾指针+循环队列):

 

#include <stdio.h>

/**
 * C 语言: 数组实现的队列,只能存储int数据。
 *
 * @author llz
 * @date 2018/7/02
 
 */

// 保存数据的数组
int arr[50];
// 队列的实际大小
int count;

//队头和队尾下标 
int front;
int tail; 


int size()
{
    return tail-front;
}

// 返回“队列”是否为空
int is_empty()
{
    if(front==tail) return 1;
    return 0;
}
// 返回“队列”是否为满
int is_full()
{
    if(front==(tail+1)%50) return 1;
    return 0;
}
// 将val添加到队列的末尾
void add(int val) 
{
    if(!is_full()) 
    {
        arr[tail] = val;
        tail=(tail+1)%50;
    }
     else
    {
        printf("队列满\n");
    }
}

// 返回“队列开头元素”
int first() 
{
    return arr[front];
}




// 返回并删除“队列开头元素”
int pop() 
{
    int ret;
    if(!is_empty())
    {
        
        ret=arr[front] ;
        front=(front+1)%50;
    }
     else
    {
        printf("队列空\n");
    }
    return ret;
}





int main() 
{
    int tmp=0,i;
    
    //输出队列中的每个元素 
    for(i=0;i<size();i++)printf("%d ",arr[i]);
       printf("\n");
    
    // 将10, 20, 30 依次推入队列中
    add(10);
    add(20);
    add(30);
    
    for(i=0;i<size();i++)printf("%d ",arr[i]);
    printf("\n");

    // 将“队列开头的元素”赋值给tmp,并删除“该元素”
    tmp = pop();
    printf("tmp=%d\n", tmp);
    

    // 只将“队列开头的元素”赋值给tmp,不删除该元素.
    tmp = first();
    printf("tmp=%d\n", tmp);

    add(40);

    // 打印队列
    printf("is_empty()=%d\n", is_empty());
    //printf("size()=%d\n", size());
    while (!is_empty())
    {
        printf("%d\n", pop());
    }
    return 0;

}

 


免责声明!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系本站邮箱yoyou2525@163.com删除。



 
粤ICP备18138465号  © 2018-2025 CODEPRJ.COM