数据结构和算法系列第3篇---队列的基本操作

前言

数据结构和算法整体框架图:

在这里插入图片描述
今天归纳总结的是线性表中的队列。

1、队列的定义

队列是只允许在一端进行插入操作,而在另一端进行删除操作的线性表。
队列是一种先进先出的线性表,简称FIFO,允许插入的一端称为队尾,允许删除的一端称为队头。

2、队列顺序存储结构—循环队列

因为线性表有顺序存储结构和链式存储结构,队列属于线性表中的一种,因此它同样具有这两种存储方式。

2.1 定义

头尾相接的顺序存储结构称为循环队列。

引入两个指针,front指针指向队头元素,rear指针指向队尾元素的下一个位置。

队列满的条件是:数组中还有一个空闲单元。总共有两种情况:rear可能比front大,也可能比front小。

在这里插入图片描述
因此队列满的条件是:(rear + 1)% QueueSize == front。
同理计算长度时,也需要考虑上述两种情况。
因此队列当前长度公式:(rear - front + QueueSize ) % QueueSize

2.2 数据结构

// 循环队列的顺序存储结构
typedef struct
{
		int data[100]; // 队列空间
		int front; // 头指针
		int rear; // 尾指针
}SqQueue;

// 循环队列的初始化代码,初始化一个空队列
Status InitQueue(SqQueue*  Q)
{
	Q->front = 0;
	Q->rear = 0;
	return OK;
}

// 求取循环队列的当前长度
int QueueLength(SqQueue* Q)
{
	return (Q->rear - Q->front + QueueSize) % QueueSize;
}

// 循环队列的入队
Status EnQueue (SqQueue* Q,int e)
{
	if ((Q->rear + 1) % QueueSize == Q->front) // 队列满判断
		return ERROR;
	Q->data[Q->rear] = e; // 将元素e赋值给队尾。
	Q->rear = (Q->rear + 1)% QueueSize; // rear指针向后移动一位,如果到最后则移动到数组头部。
	return OK;
}

// 循环队列的出队操作
Status DeQueue (SqQueue* Q,int* e)
{
	if (Q->front == Q->rear) // 队列空判断
	{
		retrun ERROR;
	}
	*e = Q->data[Q->front];
	Q->front= (Q->front+ 1)% QueueSize; // front指针向后移动一位,如果到最后则移动到数组头部。
	return OK;
}

如果但是顺序存储结构,不是循环队列,算法的时间性能不高。但是循环队列面临数组可能会溢出的问题。这是它的不足。

3、队列链式存储结构

3.1 定义

队列的链式存储结构,其实就是线性表的单链表,只不过只能尾进头出。将队头指针指向链表的头结点,队尾指针指向终端节点。如下图所示
在这里插入图片描述
空队列时,front和rear都指向头结点,如下图所示

在这里插入图片描述

3.2 数据结构

typedef struct QNode // 节点结构
{
	int data;
	struct QNode* next;
}QNode,*QueuePtr;
typedef struct // 队列的链表结构
{
	QueuePtr front,rear; // 队头、队尾指针
}LinkQueue;

3.2.1 入队

在这里插入图片描述

// 入队操作 -- 尾插
Status EnQueue (LinkQueue* Q,int e)
{
	QueuePtr s = malloc (sizeof (QNode));
	if (!s)
	{
		exit(OVERFLOW);
	}
	s->data  = e;
	s->next  = NULL;
	Q->rear->next = s;
	Q->rear = s;
	return OK;
}

3.2.2 出队

在这里插入图片描述


// 出队操作
Status DeQueue(LinkQueue* Q,int* e)
{
	QueuePtr  p;
	if (Q->front == Q->rear)
		return EEROR;
	p = Q->front->next; // 想要删除的节点
	*e = p->data; // 想要删除的节点的值
	Q->front->next = p->next; // 更新队列头结点的后继节点
	if (Q->rear == p) // 如果队列中队头即是队尾,则删除后需要更新队尾节点,将其指向头结点,如上图中右图所示
	{
		Q->rear = Q->front;
	}
	free(p);
	return OK;
}

4、实战

4.1 用栈实现队列

1、题目描述:

在这里插入图片描述
解决方案:

//队列链式存储结构节点
typedef struct QueueNode{ 
    int data;
    struct QueueNode* next;
}node,*QueuePtr;

// 队列的链表结构
typedef struct {
    QueuePtr front,rear;
} MyQueue;

/** Initialize your data structure here. */

MyQueue* myQueueCreate() {
    QueuePtr queue1 = malloc(sizeof(node)); // 头结点
    queue1->next = NULL; // 空队列头节点的next指向为NULL
    MyQueue* queue = malloc(sizeof (MyQueue));
    queue->front =  queue->rear = queue1;// 空队列的条件
    return queue;

}

/** Push element x to the back of queue. */
// 建议记忆入队的常规操作。
void myQueuePush(MyQueue* obj, int x) {
    QueuePtr MyQueuenode = malloc(sizeof(node));
    MyQueuenode->data = x;
    MyQueuenode->next = NULL; // 给申请的新节点赋值
    obj->rear->next = MyQueuenode; // 因为是队列,所以采用尾插
    obj->rear = MyQueuenode;
}

/** Removes the element from in front of queue and returns that element. */
int myQueuePop(MyQueue* obj) {
   if (obj->front == obj->rear)
   {
       return -1;
   }
    QueuePtr Queuenode = obj->front->next;
    int data = Queuenode->data;
    obj->front->next = Queuenode->next;
    if (Queuenode == obj->rear) { 
        obj->rear = obj->front; // 出队操作的注意事项
    } 
    free(Queuenode);
    return data;
}

/** Get the front element. */
int myQueuePeek(MyQueue* obj) {
    return obj->front->next->data;
}

/** Returns whether the queue is empty. */
bool myQueueEmpty(MyQueue* obj) {
    return obj->front == obj->rear;
}

void myQueueFree(MyQueue* obj) {
    while ( obj->front->next != NULL && obj->front != obj->rear){ 
        QueuePtr p = obj->front->next;
        obj->front->next = p->next;
        free(p);
    }
}

/**
 * Your MyQueue struct will be instantiated and called as such:
 * MyQueue* obj = myQueueCreate();
 * myQueuePush(obj, x);
 
 * int param_2 = myQueuePop(obj);
 
 * int param_3 = myQueuePeek(obj);
 
 * bool param_4 = myQueueEmpty(obj);
 
 * myQueueFree(obj);
*/

4.2 用队列实现栈

题目描述:
在这里插入图片描述
解决方案:

typedef struct listNode{
    int data;
    struct listNode* next;
}ListNode;


typedef struct {
    struct listNode* top; // 栈属性
    int count;

} MyStack;

/** Initialize your data structure here. */

MyStack* myStackCreate() {
    MyStack* Stacks = calloc(1,sizeof (MyStack));
    Stacks->top = NULL;
    Stacks->count = 0; // 空栈
    return Stacks;
}

/** Push element x onto stack. */
void myStackPush(MyStack* obj, int x) {
    ListNode* node = malloc (sizeof (ListNode));
    node->data = x;
    node->next = obj->top;
    obj->top = node;
    obj->count++;
}

/** Removes the element on top of the stack and returns that element. */
int myStackPop(MyStack* obj) {
        ListNode* node = obj->top;
        obj->top = obj->top->next;
        obj->count--;
        int a = node->data;
        free(node);
        return a;
   
}

/** Get the top element. */
int myStackTop(MyStack* obj) {
    return obj->top->data;

}

/** Returns whether the stack is empty. */
bool myStackEmpty(MyStack* obj) {
    return obj->count == 0;

}

void myStackFree(MyStack* obj) {
    ListNode* p;
    while (obj->count > 0)
    {
        p = obj->top;
        obj->top = obj->top->next;
        obj->count--;
        free(p);
    }

}

/**
 * Your MyStack struct will be instantiated and called as such:
 * MyStack* obj = myStackCreate();
 * myStackPush(obj, x);
 
 * int param_2 = myStackPop(obj);
 
 * int param_3 = myStackTop(obj);
 
 * bool param_4 = myStackEmpty(obj);
 
 * myStackFree(obj);

5、栈和队列的总结对比

参考之前的系列博文
数据结构和算法系列第2篇—栈的基本操作

栈:限定在表尾进行插入和删除操作的线性表。
队列:限定在一端进行插入操作,而在另一端进行删除操作的线性表。
他们都可以用线性表的顺序存储结构和链式存储结构来实现。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值