队列的实现及其应用

队列的概念

        队列:只允许在一端进行插入数据操作,在另一端进行删除数据操作,具有先进先出的效果。入数据的一段称为队尾,出数据的一段称为队头。队列的应用是:1.维持公平性(抽号)、2.广度优先遍历。

队列的应用

1.抽号

        先排队的人先入队列,这样可以保证先进先出,从而保证公平性

2.广度优先遍历(好友推荐算法)

        

        将用户的好友入队列,在知道用户好友数量的前提下,将好友出队列。

队列的实现

        如果要想实现队列,我们发现数组无法实现,原因是每次出入队列都要进行数据的挪动,这样会消耗很多空间,故我们用链表实现。

1.队列创建及目录

typedef int QDataType;

typedef struct QueneNode
{
	struct QueneNode* next;
	QDataType val;
}QNode;

typedef struct Quene//避免用到二级指针与多指针,将其封装到结构体里
{
	QNode* phead;
	QNode* ptail;
	QDataType size;
}Queue;

//初始化
void QueueInit(Queue* pq);
//入队列
void QueuePush(Queue* pq, QDataType x);
//出队列
void QueuePop(Queue* pq);
//求数据个数
int QueueSize(Queue* pq);
//取队头数据
QDataType QueueFront(Queue* pq);
//取队头数据
QDataType QueueBack(Queue* pq);
//判空
bool QueueEmpty(Queue* pq);
//队列的销毁
void QueueDestroy(Queue* pq);

        在这里,我们为了防止函数中创建二级指针,于是选择再单独创建一个结构体Quene来存放一级指针;除此之外,在传递指针时我们也可以只传递一个结构体变量。

2.队列的初始化

void QueueInit(Queue* pq)
{
	assert(pq);
	pq->phead = NULL;
	pq->ptail = NULL;
	pq->size = 0;
}

3.入队列

        我们仍然选择在入队列时创建新节点,最后一定不要忘了size++.

void QueuePush(Queue* pq, QDataType x)
{
	assert(pq);
	QNode* newnode = (QNode*)malloc(sizeof(QNode));
	if (newnode == NULL)
	{
		perror("malloce fail!");
		return;
	}
	newnode->next = NULL;
	newnode->val = x;

	if (pq->ptail == NULL)
	{
		pq->ptail = pq->phead = newnode;
	}
	else
	{
		pq->ptail->next = newnode;
		pq->ptail = newnode;
	}
	pq->size++;
}

4.出队列

        在出队列函数中,我们要注意节点为0和节点为1时要单独进行考虑。

void QueuePop(Queue* pq)
{
	assert(pq);
	assert(pq->size != 0);//0个节点
	if (pq->phead->next == NULL)//一个节点
	{
		free(pq->phead);
		pq->phead = pq->ptail = NULL;
	}
	else//多个节点
	{
		QNode* next = pq->phead->next;
		free(pq->phead);
		pq->phead = next;
	}
	pq->size--;//队列减少直接size--即可
}

5.求数据个数

int QueueSize(Queue* pq)
{
	assert(pq);
	return pq->size;
}

        由于我们在创建结构体时已经为size单独创建了一个变量size,所以这里只需将其返回即可.

6.取队头数据

QDataType QueueFront(Queue* pq)
{
	assert(pq);
	assert(pq->phead);
}
	

        取队头数据时就要注意对队头判空.

7.取队尾数据

QDataType QueueBack(Queue* pq)
{
	assert(pq);
	assert(pq->ptail);

	return pq->ptail->val;
}

        取队尾数据仍然不要忘记对队尾判空

8.队列判空

bool QueueEmpty(Queue* pq)
{
	assert(pq);

	return pq->size == 0;
}

        如果pq的size等于0,则返回true,否则返回false.

9.队列的销毁

void QueueDestroy(Queue* pq)
{
	assert(pq);
	QNode* cur = pq->phead;
	while (cur)
	{
		QNode* next = cur->next;
		free(cur);
        //先释放,再移动cur从而实现队列的销毁
		cur = next;
	}
	pq->phead = pq->ptail = NULL;
	pq->size = 0;
}

10.队列的打印

        与栈类似,如果我们要打印队列,同样是要每打印一个数,就要对其进行一次删除操作,代码如下:

int main()
{
	Queue q;
	QueueInit(&q);
	QueuePush(&q, 1);
	QueuePush(&q, 2);
	QueuePush(&q, 3);
	QuenuePush(&q, 4);

	while (!QueueEmpty(&q))
	{
		printf("%d ", QueueFront(&q));
		QueuePop(&q);
	}
	printf("\n");
	return 0;
}

队列的应用

用队列实现栈

       在这里我们用两个队列实现栈,保持一个存数据,一个为空,入数据入不为空的队列,出数据就通过空队列.

通过结构体创建两队列

typedef struct {
    Queue q1;
    Queue q2;
} MyStack;

栈的初始化

对于栈的初始化,我们首先仍然要创建内存(malloc).

然后对q1q2分别初始化:

MyStack* myStackCreate() //初始化Init
{
    MyStack* obj = (MyStack*)malloc(sizeof(MyStack));//malloc了内存可以一直存在
    QueueInit(&obj->q1);
    QueueInit(&obj->q2);

    return obj;
}

栈的插入

由于队列的特点是先进后出,所以对于栈的插入来说可以直接将q1q2里的值插入即可。要记得判空。

void myStackPush(MyStack* obj, int x) {
    if(!QueueEmpty(&obj->q1))
    {
        QueuePush(&obj->q1, x);
    }
    else
    {
        QueuePush(&obj->q2, x);
    }
}

栈的删除

对于代码的if else语句,我们还可以用假设法对其进行优化,假设q1为空,q2不为空,然后将队列中的元素导走。

int myStackPop(MyStack* obj) {
    //假设法
    Queue* empty = &(obj->q1);
    //不为空的前size-1个导走
    Queue* nonempty = &(obj->q2);
    if(!QueueEmpty(&obj->q1))
    {
        nonempty = &obj->q1;
        empty = &obj->q2;
    }
    while(QueueSize(nonempty) > 1)
    {
        QueuePush(empty,QueueFront(nonempty));
        QueuePop(nonempty);
    }
    int top = QueueFront(nonempty);
    QueuePop(nonempty);
    return top;
}

取栈顶元素

若要取栈顶元素,则直接将队列尾return 即可

int myStackTop(MyStack* obj) {
    if(!QueueEmpty(&obj->q1))//队尾直接返回即可
    {
        return QueueBack(&(obj->q1));
    }
    else
    {
        return QueueBack(&(obj->q2));
    }
}

判空

使用"&&"操作符,两队列都为空则返回空,有一个不为空则不返回空。

bool myStackEmpty(MyStack* obj) {
    return QueueEmpty(&(obj->q1))&&QueueEmpty(&(obj->q2));
}

栈的释放

对于栈的释放,要先对两队列依次进行释放,再将obj直接释放

void myStackFree(MyStack* obj) {
    //不能直接释放obj
    QueueDestroy(&obj->q1);
    QueueDestroy(&obj->q2);
    free(obj);
}

        如果觉得我写的不错,别忘了点赞收藏+评论!

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值