ACM-队列(1)

队列——计算机术语

队列是一种特殊的线性表,特殊之处在于它只允许在表的前端(front)进行删除操作,而在表的后端(rear)进行插入操作,和栈一样,队列是一种操作受限制的线性表。进行插入操作的端称为队尾,进行删除操作的端称为队头。

1> 队列是一种线性存储结构。

1.又称为先进先出线性表,即“FIFO”——first in first out;
2.在队尾(表的后端-rear)进行插入操作,在队头(表的前端-front)进行删除操作;
3.分为顺序队列循环队列

2>队列的相关概念:

1.队尾队头;
2.顺序队列循环队列;
3.队列的数组实现与链表实现;

3>顺序队列

(1)队头不动,出队列时队头后的所有元素向前移动;
PS:此操作在出队元素较多时会搬动大量元素!
(2)队头移动,出队列时队头向后移动一个位置;
PS:容易造成假上溢现象;
(3)循环队列=顺序队列的升级版;

()顺序队列中的溢出现象:(www.baidu.com)
(1) "下溢"现象:当队列为空时,做出队运算产生的溢出现象。“下溢”是正常现象,常用作程序控制转移的条件。
(2)"真上溢"现象:当队列满时,做进栈运算产生空间溢出的现象。“真上溢”是一种出错状态,应设法避免。
(3)"假上溢"现象:由于入队和出队操作中,头尾指针只增加不减小,致使被删元素的空间永远无法重新利用。当队列中实际的元素个数远远小于向量空间的规模时,也可能由于尾指针已超越向量空间的上界而不能做入队操作。该现象称为"假上溢"现象。

4>循环队列

更简单的算法实现队列!不会出现下溢、真上溢、假上溢等顺序队列中出现的溢出现象。

5>队列的常用操作

(1)初始化队列:Init_Queue(q) ,初始条件:队q 不存在。操作结果:构造了一个空队;
(2)入队操作: In_Queue(q,x),初始条件: 队q 存在。操作结果: 对已存在的队列q,插入一个元素x 到队尾,队发生变化;
(3)出队操作: Out_Queue(q,x),初始条件: 队q 存在且非空,操作结果: 删除队首元素,并返回其值,队发生变化;
(4)读队头元素:Front_Queue(q,x),初始条件: 队q 存在且非空,操作结果: 读队头元素,并返回其值,队不变;
(5)判队空操作:Empty_Queue(q),初始条件: 队q 存在,操作结果: 若q 为空队则返回为1,否则返回为0。

6>例题

此博主写的好像是c语言的例子,汗|

/*****************************************/
//LQueue.h

typedef int QElemType;
//typedef struct BTNode* QElemType;
typedef struct QNode
{
	QElemType data;
	struct QNode *_pNext;
}QNode;

typedef struct LQueue
{
	QNode *pFront;
	QNode *pRear;
}LQueue;


//初始化
void LQueueInit(LQueue *q);

//入队列
void LQueuePush(LQueue *q, QElemType data);

//出队列
void LQueuePop(LQueue *q);

//返回队头元素
QElemType LQueueTop(LQueue *q);

//返回返回队列长度
int LQueueSize(LQueue *q);

//队列是否为空
int LQueueEmpty(LQueue *q);


/************************************************/
//LQueue.c

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

//创建新结点
static QNode *BuyLQNode(QElemType data)
{
	QNode *pLQNode = (QNode *)malloc(sizeof(QNode));
	if (NULL == pLQNode)
	{
		printf("申请空间失败!\n");
		assert(pLQNode);
	}
	pLQNode->data = data;
	pLQNode->_pNext = NULL;

	return pLQNode;
}

//初始化
void LQueueInit(LQueue *q)
{
	assert(q);
	q->pFront = q->pRear = NULL;
}

//入队列
void LQueuePush(LQueue *q, QElemType data)
{
	assert(q);
	if (NULL == q->pFront)//这是一个很好的习惯!将常量写在==前面
	{
		q->pFront = q->pRear = BuyLQNode(data);
		return;
	}
	q->pRear->_pNext = BuyLQNode(data);
	q->pRear = q->pRear->_pNext;
}

//出队列
void LQueuePop(LQueue *q)
{
	assert(q);
	QNode *pDel;
	if (NULL == q->pFront)
	{
		return;
	}

	if (q->pFront == q->pRear)
	{
		q->pRear = NULL;
	}

	pDel = q->pFront;
	q->pFront = q->pFront->_pNext;
	free(pDel);
}

//返回队头元素
QElemType LQueueTop(LQueue *q)
{
	assert(q);
	return q->pFront->data;
}

//返回队尾元素
QElemType LQueueBack(LQueue *q)
{
	assert(q);
	return q->pRear->data;
}

//返回返回队列长度
int LQueueSize(LQueue *q)
{
	int count = 0;
	QNode *pCur;
	assert(q);
	pCur = q->pFront;
	while (pCur)
	{
		pCur = pCur->_pNext;
		count++;
	}
	return count;
}

//队列是否为空
int LQueueEmpty(LQueue *q)
{
	return NULL == q->pFront;
}

汗,这个代码竟然是用链表实现的顺序队列(2),说实话呢有点LOW。让我们将这个代码高富帅(循环)起来!

以下1-5代码来源:百度百科-队列

(1)将循环队列置为空

//将队列初始化
SeQueue::SeQueue()
{ front=0;
rear=0;
cout<<"init!"<<endl;//当front == rear时队列为空;
}

(2)判断循环队列是否为空

int SeQueue::Empty()
{ if(rear==front) return(1);
else return(0);
}

(3)在循环队列中插入新的元素x

void SeQueue::AddQ(ElemType x)
{ if((rear+1) % MAXSIZE==front) cout<<" QUEUE IS FULL! "<<endl;
else{ rear=(rear+1) % MAXSIZE;
elem[rear]=x;
cout<<" OK!";
}//这样队列可以循环使用。
}

(4)删除队列中队首元素

ElemType SeQueue::DelQ()
{ if(front==rear)
{ cout<<" QUEUE IS EMPTY! "<<endl; return -1;}
else{ front=(front+1) % MAXSIZE;
return(elem[front]);
}
}

(5)取队列中的队首元素(进行删除操作的一端\表的前端)

ElemType SeQueue::Front()
{ ElemType x;
if(front== rear)
cout<<"QUEUE IS EMPTY "<<endl;
else x= elem[(front+1)%MAXSIZE];
return (x);
}

7>实敲分析

循环队列的数组实现:(原创)

#include <iostream>
//循环队列de数组实现
using namespace std;

struct seQueue{
    int front;
    int rear;
    enum{MaxSize = 100};
    int data[MaxSize];
    seQueue();
    ~seQueue();
    bool isEmpty();
    void inQueue(int x);
    int outQueue();
    int getFront();
};

seQueue :: seQueue()
{
    front = rear = 0;
    cout << "完成初始化!"<< endl;
}

seQueue :: ~seQueue(){}

bool seQueue :: isEmpty()
{
    return (front == rear);
}

void seQueue :: inQueue(int x)
{
    if((rear + 1) % MaxSize == front)
        {
            cout << "栈已满!"<< endl;
            return;
        }
    rear = (rear + 1) % MaxSize;
    data[rear] = x;
}

int seQueue :: outQueue()
{
    if(rear==front)
    {
        cout << "栈已空!" << endl;
        return 0;
    }
    front = (front + 1) % MaxSize;
    return data[front];
}

int seQueue :: getFront()
{
    if(rear==front)
    {
        cout << "栈已空!" << endl;
        return 0;
    }
    return data[(front + 1) % MaxSize];//为什么要加1
}

int main()
{
    seQueue q;
    for(int i = 0;i < 10; i++)
        q.inQueue(i);
    cout << q.getFront() << endl;
    for(int i = 0;i < 10; i++)
        cout << q.outQueue() << endl;
    cout << "栈空:" << q.isEmpty() << endl;
    return 0;
}

这个程序无法实现在main函数里进行测试,仍需要升级!

#include <iostream>
//循环队列de数组实现
using namespace std;

struct seQueue{
    int front;
    int rear;
    enum{MaxSize = 11};//考虑到开始时data[0]不会存放数据;
    int data[MaxSize];
    seQueue();
    ~seQueue();
    bool isEmpty();
    void inQueue(int x);
    int outQueue();
    int getFront();
};

seQueue :: seQueue()
{
    front = rear = 0;
    cout << "完成初始化!"<< endl;
}

seQueue :: ~seQueue(){}

bool seQueue :: isEmpty()
{
    return (front == rear);
}

void seQueue :: inQueue(int x)
{
    if((rear + 1) % MaxSize == front)
        {
            cout << "栈已满!"<< endl;
            return;
        }
    rear = (rear + 1) % MaxSize;
    data[rear] = x;
}

int seQueue :: outQueue()
{
    if(rear==front)
    {
        cout << "栈已空!" << endl;
        return 0;
    }
    front = (front + 1) % MaxSize;
    return data[front];
}

int seQueue :: getFront()
{
    if(rear==front)
    {
        cout << "栈已空!" << endl;
        return 0;
    }
    return data[(front + 1) % MaxSize];//为什么要加1
}

int main()
{
    seQueue q;
    int i;
    for(i = 0;i < 10; i++)
        q.inQueue(i);
    q.inQueue(i);//此处应显示栈已满;
    cout << q.getFront() << endl;
    cout << q.outQueue() << endl;//出1个;
    q.inQueue(i);//此处应当可添加;
    for(i = 0;i < 10; i++)
        cout << q.outQueue() << endl;
    cout << "栈空:" << q.isEmpty() << endl;
    return 0;
}

再升级版:循环队列的链表实现:
(大佬的代码https://blog.youkuaiyun.com/u012922219/article/details/52626039)还没看懂o(╥﹏╥)o

public class LinkedQueue implements QueueADT {
	
	// 链表节点类,内部类实现,包含数据域和指针域
	class Node {
		int data; // 数据域
		Node next;// 指针域
 
		public Node(int data) {
			this.data = data;
		}
	} 
	//指向队首、队尾节点
	private Node front,rear;
	//维护队列的大小
	private int count;
	
	public LinkedQueue() {
		front=null;
		rear=null;
		count=0;
	}
	//保证出栈或者出队列的是头结点,这样才能完成更新,所以对于栈的入栈操作,是从尾节点开始构造的
	//队列的入队操作,是从头结点开始构造的
	@Override
	public void enqueue(Object element) {
		Node node=new Node((Integer)element);
		if(isEmpty()){
			front=rear=node;
		}else{
			rear.next=node;
			rear=node;
		}
		count++;
	}
	
	@Override
	public Object dequeue() {
		if(isEmpty()){
            System.out.println("队列中没有元素");
            System.exit(1);
        }
		Object result=front.data;
		front=front.next;
		count--;
		return result;
	}
 
	@Override
	public Object first() {
		return front.data;
	}
 
	@Override
	public boolean isEmpty() {
		return (size()==0);
	}
 
	@Override
	public int size() {
		return count;
	}
	
	public static void main(String[] args) {
        
        LinkedQueue queue = new LinkedQueue();
        
        System.out.println("依次将0到9入队,然后连续出队5次");
        for(int i = 0;i < 10;i++)
            queue.enqueue(i);
        for(int i = 0;i < 5;i++)
            queue.dequeue();
        System.out.println("队列的大小为: " + queue.size());
        System.out.println("队列为空吗?: " + queue.isEmpty());
        System.out.println("队列的头为: " + queue.first());
 
    }

8>总结

1.堆:什么是堆?又该怎么理解呢?
①堆通常是一个可以被看做一棵树的数组对象。堆总是满足下列性质:
·堆中某个节点的值总是不大于或不小于其父节点的值;
·堆总是一棵完全二叉树。
将根节点最大的堆叫做最大堆或大根堆,根节点最小的堆叫做最小堆或小根堆。常见的堆有二叉堆、斐波那契堆等。
②堆是在程序运行时,而不是在程序编译时,申请某个大小的内存空间。即动态分配内存,对其访问和对一般内存的访问没有区别。
③堆是应用程序在运行的时候请求操作系统分配给自己内存,一般是申请/给予的过程。
④堆是指程序运行时申请的动态内存,而栈只是指一种使用堆的方法(即先进后出)。
2.队列对于解决约瑟夫环问题有一定的帮助。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值