【数据结构】队列

目录

1.1 特性

1.2 循环队列

1.3 链式队列


 

1.1 特性

队列是只允许再两端进行插入和删除操作的线性表,在队尾插入,在队头删除,插入的一段被称为“队尾”,删除的一端被称为“队头”。队列包括顺序队列(循环队列)、链式队列。

结构:先进先出FIFO

操作:创建、入列、出列、判空和满、清空

注意:为了避免假溢出问题,即队列前面还有空闲,但是队尾已经出现越界,所以在实际使用队列时,为了使队列空间能重复使用,往往对队列的使用方法稍加改进,需要引入循环队列。

循环队列是把顺序队列首尾相连,把存储队列元素的表从逻辑上看成一个环,成为循环队列。

1.2 循环队列

逻辑结构:线性结构

存储结构:顺序存储结构

操作:创建、入列、出列、判空、判满、清空

typedef int datatype;
typedef struct sequeue
{
    datatype data[N]; //循环队列的数组
    int rear;         //入队端
    int front;        //出队端
}sequeue_t;

创空:

入列:

出列:

#include <stdio.h>
#include <stdlib.h>

#define N 5
typedef int datatype;
typedef struct sequeue
{
    datatype data[N]; //循环队列的数组
    int rear;         //入队端
    int front;        //出队端
} sequeue_t;

//创建一个空的队列
sequeue_t *createEmptySequeue()
{
    //1.开辟结构体大小空间
    sequeue_t *p = (sequeue_t *)malloc(sizeof(sequeue_t));
    if (NULL == p)
    {
        perror("p malloc err");
        return NULL;
    }
    //2.初始化
    p->front = 0; //使用的时候都是数组的下标
    p->rear = 0;
    return p;
}

//判断队列是否为满
int isFullSequeue(sequeue_t *p)
{
    //思想上,舍去数组上的一个存储位置,用于判断队列是否为满(否则和判空区分不了)
    //先判断rear的下一个位置是否等于front
    return (p->rear + 1) % N == p->front;
}

//入列 data代表入列的数据
int inSequeue(sequeue_t *p, datatype data)
{
    //1. 判满
    if (isFullSequeue(p))
    {
        printf("is full\n");
        return -1;
    }
    //2. 将数据入列
    p->data[p->rear] = data;
    //3. 将尾向后移动一个单位
    p->rear = (p->rear + 1) % N; //0 1 2 3 4循环
}

//判断队列是否为空
int isEmptySequeue(sequeue_t *p)
{
    return p->rear == p->front;
}

//出列
datatype outSequeue(sequeue_t *p)
{
    //1.判空
    if (isEmptySequeue(p))
    {
        printf("isEmptySequeue\n");
        return -1;
    }
    //2.设临时变量保存队头数据
    datatype temp = p->data[p->front];
    //3.将队头向后移动一个单位
    p->front = (p->front + 1) % N;
    //4.返回刚才保存的要出列的数据
    return temp;
}

//求队列的长度
int lengthSequeue(sequeue_t *p)
{
    //长度情况分为 rear >= front  和 rear < front
    //rear == front 就是空队列长度为0
    // if (p->rear >= p->front)
    //     return p->rear - p->front;
    // else
    //     return p->rear - p->front + N;

    return (p->rear - p->front + N) % N;
}

//清空队列函数
void clearSequeue(sequeue_t *p)
{
    //思想:只要不为空就出列
    while (!isEmptySequeue(p))
        printf("%d ", outSequeue(p)); 
    printf("\n");
}

int main(int argc, char const *argv[])
{
    sequeue_t *p = createEmptySequeue();
    for (int i = 0; i < 5; i++)
        inSequeue(p, i);//最后一个元素装不进,会满
    clearSequeue(p);    //0 1 2 3
    return 0;
}

循环队列,如果数组的元素个数为N,那么队列中最多能够存储的数据数的多少?N-1 个

为什么?

答:rear后面队尾插入的时候,需要先判满:用rear的后一个元素和front判断如果相等就是满。也就是用队尾的后一个和头去做判断,用于判满,这样会造成一个空间的浪费。

1.3 链式队列

逻辑结构:线性结构

存储结构:链式存储

操作:创建、入列、出列、判空、清空

typedef int datatype;
typedef struct node
{
    datatype data;
    struct node *next;
}link_node_t,*link_node_p;

//将队列头指针和尾指针封装到一个结构体里
typedef struct queue
{
    link_node_p front;  //相当于队头的指针
    link_node_p rear;   //相当于队尾的指针
}linkqueue_t;  
//有了头尾指针就可以操作这个链表了,并且记录头和尾就可以实现队列的功能。

创空:

入列:

#include <stdio.h>
#include <stdlib.h>

typedef int datatype;
typedef struct node
{
    datatype data;
    struct node *next;
} link_node_t, *link_node_p;

//将队列头指针和尾指针封装到一个结构体里
typedef struct queue
{
    link_node_p front; //相当于队头的指针
    link_node_p rear;  //相当于队尾的指针
} linkqueue_t;
//有了头尾指针就可以操作这个链表了,并且记录头和尾就可以实现队列的功能。

//创建一个空的队列,用有头链表。
linkqueue_t *createEmptyLinkQueue()
{
    //1.申请队列结构体空间
    linkqueue_t *p = (linkqueue_t *)malloc(sizeof(linkqueue_t));
    if (NULL == p)
    {
        perror("p malloc err");
        return NULL;
    }
    //2. 初始化,将头尾指针指向头节点。
    p->front = p->rear = (link_node_p)malloc(sizeof(link_node_t));
    if (NULL == p->front) //用任意一个做判断就可以,因为他俩都指向头节点
    {
        perror("p->front malloc err");
        return NULL;
    }
    //3. 初始化头节点
    p->front->next = NULL; //注意:先用头或尾指针找到头节点,再找头节点中的指针域next
    return p;
}

//入列 data代表入列的数据
int inLinkQueue(linkqueue_t *p, datatype data)
{
    //1.创建一个新节点
    link_node_p pnew = (link_node_p)malloc(sizeof(link_node_t));
    if (NULL == pnew)
    {
        perror("pnew malloc err");
        return -1;
    }
    //2.初始化新节点
    pnew->data = data;
    pnew->next = NULL;

    //3.链接新节点到链表尾巴
    p->rear->next = pnew;
    //4.移动尾指针到新节点
    p->rear = pnew; //rear保证一直指向当前链表的尾巴
    return 0;
}

//判断队列是否为空
int isEmptyLinkQueue(linkqueue_t *p)
{
    return p->front == p->rear;
}

//出列
//思想:每次释放front所指节点,然后移动front到后一个节点返回当前节点数据
datatype outLinkQueue(linkqueue_t *p)
{
    //1.判空
    if (isEmptyLinkQueue(p))
    {
        printf("isEmptyLinkQueue\n");
        return -1;
    }
    //2.定义指针pdel指向当前p->front所指节点
    link_node_p pdel = p->front;
    //3.将front 向后移动一个单位,去做新的头节点
    p->front = p->front->next;
    //4.释放pdel
    free(pdel);
    //5.将当前front所指节点中数据出列,那此时这个节点为新的头节点,因为数据域无效。
    return p->front->data;
    return 0;
}

//清空
void clearLinkQueue(linkqueue_t *p)
{
    while (!isEmptyLinkQueue(p))
        printf("%d ", outLinkQueue(p));
    printf("\n");
}

//求队列长度的函数
int lengthLinkQueue(linkqueue_t *p)
{
    int len = 0;
    link_node_p h = p->front;
    while (h->next != NULL)
    {
        len++;
        h = h->next;
    }
    return len;
}

int main(int argc, char const *argv[])
{
    linkqueue_t *p = createEmptyLinkQueue();
    for (int i = 1; i <= 5; i++)
        inLinkQueue(p, i);
    printf("len=%d\n", lengthLinkQueue(p));
    while (!isEmptyLinkQueue(p))
        printf("%d ", outLinkQueue(p)); //1 2 3 4 5
    printf("\n");
    return 0;
}

用两个栈实现一个队列的功能?简述算法和思路

思路:设两个栈,一个用于in一个用于out

实现入队: 如果in栈没满直接入栈,如果满了出栈之后入out栈,这样先入栈的会在out栈的栈顶。

实现出列: 如果out栈不为空直接让数据出栈,如果为空则把in栈的数据依次出栈入out栈, 这样先入栈的数据会在out栈顶。然后再把数据出out栈,来实现出列功能。

这样可以实现队列的先入先出顺序功能。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

WeeJot

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

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

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

打赏作者

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

抵扣说明:

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

余额充值