队列的讲解和实现(图解+代码/C语言+循环队列OJ)

今天为大家分享的是队列的实现,本文主要介绍单向链表来模拟实现队列,最后给出力扣上一道循队列的OJ实现。

目录

 一、图解队列的结构

二、模拟栈的分步实现

(1)队列的初始化

(2)队尾入队

(3)队头出队

(4)获取队头队尾元素

(5)获取队列中有效元素个数

(6)检测队列是否为空

(7)销毁队列

三、循环链表OJ实现

(1)OJ链接

(2)描述

(3)函数实现

Ⅰ. C语言实现

Ⅱ. C++实现


 一、图解队列的结构

对于队列,我们可以用八个字概括“先入先出,后入后出”。“队列”顾名思义就是像公交车排队的队伍一样,排队的时候,先进入到队伍之中的人先完成排队,后进入到队伍之中的人最后才能出去。

二、模拟栈的分步实现

(1)队列的初始化

通过对队列结构的描述我们可以很容易得出,我们会经常对队列的头尾元素进行操作,因此我们需要用两个指针来分别记录头结点和尾结点的位置。在初始化的时候只需要将两个指针置空即可。

// 链式结构:表示队列 
typedef int QDataType;

typedef struct QListNode
{
	struct QListNode* _next;
	QDataType _data;
}QNode;

// 队列的结构 
typedef struct Queue
{
	QNode* _front;
	QNode* _rear;
}Queue;

// 初始化队列 
void QueueInit(Queue* q)
{
	assert(q);
	q->_front = q->_rear = NULL;
}

(2)队尾入队

队列只能从队尾进入,不然就是“插队”,是不可以的哦。入队不仅需要改变尾指针,同样有可能改变头指针,即当队列为空时就需要将头指针和尾指针都指向新的结点。

// 队尾入队列 
void QueuePush(Queue* q, QDataType data)
{
	assert(q);
	QNode* new_node = (QNode*)malloc(sizeof(QNode));
	assert(new_node);
	new_node->_data = data;
	new_node->_next = NULL;
	if (q->_rear == NULL)
		q->_rear = q->_front = new_node;
	else
	{
		q->_rear->_next = new_node;
		q->_rear = q->_rear->_next;
	}
}

(3)队头出队

同样类比于队尾出队,出队只能从队头出,同时两个指针都有可能发生改变。

// 队头出队列 
void QueuePop(Queue* q)
{
	assert(q);
	assert(!QueueEmpty(q));
	QNode* del_node = q->_front;
	if (q->_front->_next == NULL)
	{
		free(q->_front);
		q->_front = q->_rear = NULL;
	}
	else
	{
		q->_front = q->_front->_next;
		free(del_node);
	}
}

(4)获取队头队尾元素

由于我们本身就记录了头尾结点的位置,因此获取头尾结点的值就很容易了,唯一需要注意的时,当队列为空的时候,就不能调用函数了。

// 获取队列头部元素 
QDataType QueueFront(Queue* q)
{
	assert(q);
	assert(!QueueEmpty(q));
	return q->_front->_data;
}

// 获取队列队尾元素 
QDataType QueueBack(Queue* q)
{
	assert(q);
	assert(!QueueEmpty(q));
	return q->_rear->_data;
}

(5)获取队列中有效元素个数

主要有两种方式可以记录队列中的有效元素个数,首先是遍历链表然后计数一边,另一个方式就是在队列的结构体中添加一个计数器记录,在每次入队和出队的时候对计数器操作,然后返回计数器的值即可。在此演示遍历链表的方式。

// 获取队列中有效元素个数 
int QueueSize(Queue* q)
{
	assert(q);
	QNode* size = q->_front;
	int len = 0;
	while (size)
	{
		len++;
		size = size->_next;
	}
	return len;
}

(6)检测队列是否为空

为了使检测更加严谨,可以同时检测头指针和尾指针,这样可以防止由于在入队和出队时忘记同时对头尾指针操作导致野指针或空指针等问题。

// 检测队列是否为空,如果为空返回非零结果,如果非空返回0 
int QueueEmpty(Queue* q)
{
	assert(q);
	return q->_front == NULL && q->_rear == NULL;
}

(7)销毁队列

销毁队列时需要注意的就是在释放完所有结点之后需要将头尾指针置空,不然会造成野指针问题。

// 销毁队列 
void QueueDestroy(Queue* q)
{
	assert(q);
	QNode* node = q->_front;
	while (q->_front)
	{
		node = q->_front;
		q->_front = q->_front->_next;
		free(node);
	}
	q->_front = q->_rear = NULL;
}

三、循环链表OJ实现

(1)OJ链接

T622 - 中等难度 - 设计循环队列 - 力扣

(2)描述

(3)函数实现

这里实现的方式是利用循环数组模拟,头尾指针也就成了头尾元素的下标,相比较于普通队列,循环队列长度固定,利用数组实现的时候需要注意移动头尾指针的时候可能发生循环,因此要采用取模运算的方式将下标控制在一定范围之内。

Ⅰ. C语言实现

typedef struct 
{
    int* arr;
    int head, tail, size, capacity;
} MyCircularQueue;


MyCircularQueue* myCircularQueueCreate(int k) 
{
    MyCircularQueue* ret = (MyCircularQueue*)malloc(sizeof(MyCircularQueue));
    ret->arr = (int*)malloc(sizeof(int) * k);
    ret->head = ret->tail = ret->size = 0;
    ret->capacity = k;
    return ret;
}

bool myCircularQueueEnQueue(MyCircularQueue* obj, int value)
{
    if (obj->size == obj->capacity)
            return false;
    obj->arr[obj->tail] = value;
    obj->tail++, obj->size++;;
    obj->tail %= obj->capacity;
    return true;
}

bool myCircularQueueDeQueue(MyCircularQueue* obj) 
{
    if (obj->size == 0)
            return false;
    obj->head++, obj->size--;
    obj->head %= obj->capacity;
    return true;
}

int myCircularQueueFront(MyCircularQueue* obj) 
{
    if (obj->size == 0)
            return -1;
    return obj->arr[obj->head];
}

int myCircularQueueRear(MyCircularQueue* obj) 
{
    if (obj->size == 0)
        return -1;
    return obj->arr[(obj->tail - 1 + obj->capacity) % obj->capacity];
}

bool myCircularQueueIsEmpty(MyCircularQueue* obj) 
{
    return obj->size == 0;
}

bool myCircularQueueIsFull(MyCircularQueue* obj) 
{
    return obj->size == obj->capacity;
}

void myCircularQueueFree(MyCircularQueue* obj) 
{
    free(obj->arr);
}

/**
    bool isFull() 
    {
        return size == capacity;
    }
};
*/

Ⅱ. C++实现

class MyCircularQueue 
{
private:
    int* arr;
    int head, tail, size, capacity;
public:
    MyCircularQueue(int k) 
    {
        arr = new int[k];
        head = tail = size = 0;
        capacity = k;
    }

    bool enQueue(int value) 
    {
        if (size == capacity)
            return false;
        arr[tail] = value;
        tail++, size++;;
        tail %= capacity;
        return true;
    }

    bool deQueue() 
    {
        if (size == 0)
            return false;
        head++, size--;
        head %= capacity;
        return true;
    }

    int Front() 
    {
        if (size == 0)
            return -1;
        return arr[head];
    }

    int Rear() 
    {
        if (size == 0)
            return -1;
        return arr[(tail - 1 + capacity) % capacity];
    }

    bool isEmpty() 
    {
        return size == 0;
    }

    bool isFull() 
    {
        return size == capacity;
    }
};

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值