数据结构:队列

一、队列


队列是一种只有入队和出队的数据结构。队列可以是单线队列、循环队列、优先队列。它是一种先进先出的数据结构。
1.线性队列:
它有两个指针:head和tail
其中head指向头元素的前一位(也可以是指向头元素),tali指向末尾元素的位置,如果我们要出队一个元素,那么就把head指针向后移动一位。如果我们要入队一个元素,也是给tail指针后面的元素赋值后,向后移动一位。
缺点:线性队列的缺点是容易造成对储存空间的浪费,如果tail指针已经走到最后面,那么即使队列中即使还有元素,也无法入队。它可以通过依次移动元素的位置解决,但太浪费时间了,因此我们使用循环队列。

2.循环队列:如它名字一样它是一个头尾相连的队列,也拥有两个指针head和tail
假设队列有n+1个空间,那么位置0我们是不存储元素的,因此实际上存储空间只有n个。
其中当队列为空的时候,head和tail它们在同一个位置0;
当入队一个元素,tail向后移动一位,为tail==tail+1,但是为了防止tail指针在n时,无法移动,因此我们实际上为tail == (tail+1)%n;
当我们插入元素,使得队列满了以后,会导致tail+1 == head;
当我们出队时,与入队一样,实际上为head == (head+1)%n;
当我们拥有一个队列时,它的实际元素个数为(tail-head+n)%n,这样的目的是为了防止head>tail;

这里的head可以叫做front、tail可以叫做rear。


3.优先队列:它是按照元素优先级排列的队列。


4.双端队列:它是一种可以在前端和后端都进行插入和删除的队列。


双端队列既可以用作堆栈,也可以用作队列,因为它允许在两端进行插入和删除操作。

二、队列的操作:


Enqueue:在后端插入元素,无返回值,或返回无效。
Dequeue:从队列前端执行删除操作。返回删除的元素。
Peek:该元素由队列中的front指针(head)指向,但不删除它。
队列溢出(isfull):表示队列完全满时的溢出情况。
队列下溢(isempty):表示队列为空时的下溢情况。
 

三、队列的实现:(以c为例)


它包括两种方式:「顺序存储的队列」 和 「链式存储的队列」。
顺序存储的队列」:利用一组地址连续的存储单元依次存放队列中从队头到队尾的元素,同时使用指针 front(head) 指向队头元素在队列中的位置,使用指针 rear(tail)指示队尾元素在队列中的位置。
链式存储的队列」:利用单链表的方式来实现队列。队列中元素按照插入顺序依次插入到链表的第一个节点之后,并使用队头指针 front(head) 指向链表头节点位置,也就是队头元素,rear(tail) 指向链表尾部位置,也就是队尾元素。
注意:front 和 rear 的指向位置并不完全固定。有时候算法设计上的方便以及代码简洁,也会使 front 指向队头元素所在位置的前一个位置。rear 也可能指向队尾元素在队列位置的下一个位置。具体还是要看算法是如何实现的。


1.队列的数组表示:front和rear初始化为0


我们可以使用线性数组轻松表示队列。有两个变量,即前和后,在每个队列的情况下实现。front和rear变量指向队列中执行插入和删除的位置。最初,front 和queue 的值为-1,表示队列为空。
由于到目前为止,队列中还没有执行删除操作,因此 front 的值仍然是 -1 。然而,每次向队列中执行插入操作时,rear 的值都会加一。将元素插入到上图所示的队列中后,队列将如下所示。后部的值将变为5,而前部的值保持不变。


插入函数: 

 void insert (int queue[], int max, int front, int rear, int item)   
{  
    if (rear + 1 == max)  
    {  
        printf("overflow");  
    }  
    else  
    {  
        if(front == -1 && rear == -1)  
        {  
            front = 0;  
            rear = 0;  
        }  
        else  
        {  
            rear = rear + 1;   
        }  
        queue[rear]=item;  
    }  

 删除函数:

 int delete (int queue[], int max, int front, int rear)  
{  
    int y;   
    if (front == -1 || front > rear)   
    {  
        printf("underflow");  
    }  
    else   
    {  
        y = queue[front];  
        if(front == rear)  
        {  
            front = rear = -1;  
            else   
            front = front + 1;   
        }  
        return y;  
    }  
}   

 2.链表实现:


具有 n 个元素的队列的链接表示的存储要求是 o(n),而操作的时间要求是 o(1)。
在链接队列中,队列的每个节点由两部分组成,即数据部分和链接部分。队列的每个元素都指向内存中紧邻的下一个元素。
在链接队列中,内存中维护着两个指针,即前指针和后指针。front 指针包含队列起始元素的地址,rear 指针包含队列最后一个元素的地址。
插入和删除分别在后端和前端进行。如果front和rear都为NULL,则表明队列为空。

 插入函数:

 void insert(struct node *ptr, int item; )  
{          
    ptr = (struct node *) malloc (sizeof(struct node));  
    if(ptr == NULL)  
    {  
        printf("\nOVERFLOW\n");  
        return;  
    }  
    else  
    {   
        ptr -> data = item;  
        if(front == NULL)  
        {  
            front = ptr;  
            rear = ptr;   
            front -> next = NULL;  
            rear -> next = NULL;  
        }  
        else   
        {  
            rear -> next = ptr;  
            rear = ptr;  
            rear->next = NULL;  
        }  
    } 
}     

 删除函数:

 void delete (struct node *ptr)  
{  
    if(front == NULL)  
    {  
        printf("\nUNDERFLOW\n");  
        return;  
    }  
    else   
    {  
        ptr = front;  
        front = front -> next;  
        free(ptr);  
    }  

四、c++中对队列的使用:

C++队列queue
1.头文件#include< queue>
2.初始化:

    queue<int>q1;
    queue<double>q2;  
    queue<char>q3;


其他初始化:


queue<char, list<char>>q1;
//用list容器实现的queue 
queue<int, deque<int>>q2;
 //用deque容器实现的queue 


3.因为queue转换器要求容器支持front()、back()、push_back()及 pop_front(),说明queue的数据从容器后端入栈而从前端出栈。所以可以使用deque和list对queue初始化,而vector因其缺少pop_front(),不能用于queue。

4.常用函数:
push() 在队尾插入一个元素
pop() 删除队列第一个元素
size() 返回队列中元素个数
empty() 如果队列空则返回true
front() 返回队列中的第一个元素
back() 返回队列中最后一个元素

五、例题:

题目:力扣(LeetCode)官网 - 全球极客挚爱的技术成长平台

class MyStack {

public:

    queue<int> queue1;//用来存储入栈的元素

    queue<int> queue2;//帮助queue1完成栈的操作

    MyStack() {

        //无,由于上面创建了

    }

   

    void push(int x) {

        queue2.push(x);

        while(!queue1.empty()){

            queue2.push(queue1.front());

            queue1.pop();

        }

        swap(queue1,queue2);

    }

   

    int pop() {//栈的pop操作是要返回值的,而队列的没有,因此要用一个值记录front,在删除

        int r = queue1.front();

        queue1.pop();

        return r;

    }

   

    int top() {//栈的top和队列的front是一致的,因此只有记录就可以了

        return  queue1.front();

    }

   

    bool empty() {

        return queue1.empty();

    }

};

//思路:用两个队列实现栈。一个队列1用来专门存储、另外一个队列2用来记录我们插入的值。我们给队列插入值,把它记录到队列2,判断队列1是否有值,如果有就把队列1的元素从前到后放入队列2,最后交换队列1和队列2.如果队列1没有值,也交换队列1和队列2.因此队列始终保持前面的元素是后进的,后面的元素是先进的。这样对于队列1的front()操作就是栈的top()操作。pop()就是pop操作,empty()操作就是empty();

  • 5
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

years_GG

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值