一、什么是队列?
1.是一种特殊的线性表
2.只允许在一端进行插入数据,在另一端进行删除数据
二、队头&队尾&入队列&出队列
1.队头:进行删除数据的一端
2.队尾:进行插入数据的一端
3.入队列:在队尾处进行插入数据的操作
4.出队列:在队尾处进行删除数据的操作
三、队列的特性
First In First Out—–FIFO
,即就是先进先出
先进先出就是指先进来的先出去,就像我们吃饭排队一样,谁早排谁就可以早买到饭且可以先走,其实排队就是一种队列
一、什么是顺序队列
顺序队列是队列的一种,只是说队列的存在形式是队列中的元素是连续的,就像数组
二、顺序队列的实现方式
一、什么是循环队列?
循环队列是首尾相接的顺序存储队列,顾名思义循环队列就是队列的内存可以循环使用
二、为什么会有循环队列?
在顺序队列中,出现了假溢出问题,那循环队列就是为了假溢出问题的,假溢出造成了内存浪费,循环队列可以使得内存得到有效的利用
三、循环队列的实现方式?
给定两个指针,分别标记队头和队尾,标记队头的指针是front,标记队尾的是rear
1.空队列
开始时,front是队头指针,rear是队尾指针,空队列时front和rear指向同一位置
2.队列中有元素
在插入元素时,rear会随着元素的插入而向后移动,front不会动,如果删除元素的话,那rear会向前移动,front也还是不会动
四、循环队列如何进行判断是否存满?或者为空呢?
ps:循环队列是空队列时,front和rear都指向队列的起始位置,随着元素的插入和删除,rear会跟着向后或者向前移动,而front是不会动的
1.少使用一个循环队列中的存储空间
就是说呢,在存储元素时,少用一个存储单元,那队列空的时候,front和rear指向同一位置,那随着插入元素,rear向后移动,最后一个存储单元不使用,所以队列满的时候,rear的下一个位置就是front指向的元素
队列为空时的判断条件:front==rear时 ,队列为空队列
队列为满时的判断条件:(rear+1)%M == front时,队列为满队列
ps:M是循环队列的空间大小,即就是M标记了循环队列中最多可以存储几个元素
本来rear+1就是front指向的位置,那为什么还要跟队列的空间大小取模呢?是因为当rear指向最后一个位置的时候,下标为7,那rear+1=8,而循环队列中没有下标为8的元素,如图可知,必须对相加的结果取模
2.给一个标记flag,默认flag为false
就是说呢,给一个标记flag ,默认此标记为false,当插入元素就把标记flag改为true,当删除元素就把标记改为false
ps:当队列为空的时候,front和rear指向同一位置,当队列为满的时候,front和rear也是指向同一位置
队列为空时的判断条件:front==rear&&flag==false
队列为满时的判断条件:front==rear&&flag==true
3.给一个计数器count
插入一个元素时count++,删除一个元素时count - -
队列为空时的判断条件:count==0
队列为满时的判断条件:count=M 或者(count>0 && rear==front)
ps:M是循环队列的空间大小
这三种方法中,第三种方法最简单方便
五、循环队列的删除和插入
1.插入
front和rear开始时都位于下标为0的位置,插入一个元素,插入的位置=rear指向的位置,插入完成后,front不动,rear向后移动一个位置,但是要注意边界情况,当rear移动到等于N的位置时,此时需要把rear归位,即就是让rear=0,从而开始下一轮的移动,每插入一个元素count加1
void Push(const T&data)//给队列中插入元素
{
if (_count == N)
return;
_array[_rear++] = data;
//_rear = (_rear + 1) % N;
//不用此种方式是因为取模的效率不高
//而且使用此种方式的话,每插入一个元素都需要取模
if (_rear == N)
_rear = 0;
_count++;
}
2.删除
删除元素时,只需要把front向后移动一个位置就好,随着元素的删除,当front==N时,注意把front置为0,每删除一个元素,count也需要减去1
void Pop()//删除元素
{
if (Empty())
return;
++_front;//删除元素时,只需要把队头的标记向后移动一个位置就好
if (_front == N)
_front = 0;//删除元素时注意边界问题
_count--;
}
源代码
#include
using namespace std;
template<class T,size_t N = 8>//N标记的是队列中的元素个数,默认为8
class Queue
{
public:
Queue()
:_front(0)
, _rear(0)
,_count(0)
{}
void Push(const T&data)//给队列中插入元素
{
if (_count == N)
return;
_array[_rear++] = data;
//_rear = (_rear + 1) % N;
//不用此种方式是因为取模的效率不高
//而且使用此种方式的话,每插入一个元素都需要取模
if (_rear == N)
_rear = 0;
_count++;
}
void Pop()//删除元素
{
if (Empty())
return;
++_front;//删除元素时,只需要把队头的标记向后移动一个位置就好
if (_front == N)
_front = 0;//删除元素时注意边界问题
_count--;
}
T&Front()//获得队头元素
{
return _array[_front];
}
const T&Front()const
{
return _array[_front];
}
T&Back()//获得队尾元素
{
return _array[(_rear - 1 + N)%N];
}
const T&Back()const
{
return _array[(_rear - 1 + N)%N];
}
bool Empty()const//判断队列是否为空
{
return _count == 0;
}
size_t Size()
{
return _count;//_count标记的是队列中元素的个数
}
private:
int _front;//用来标记队头
int _rear;//用来标记队尾
T _array[N];//数组是用来存储顺序队列中的元素的
size_t _count;//是用来标记队列是否满的
//count=0时队列为空,count=N表示队列已满
};
void TestQueue()
{
Queue<int,8> q;
q.Push(1);
q.Push(2);
q.Push(3);
q.Push(4);
q.Push(5);
cout << "队头元素为:>" << q.Front() << endl;
cout << "队尾元素为:>" << q.Back() << endl;
cout << "队列的元素个数为:>" << q.Size() << endl;
q.Pop();
q.Pop();
cout << "队头元素为:>" << q.Front() << endl;
cout << "队尾元素为:>" << q.Back() << endl;
cout << "队列的元素个数为:>" << q.Size() << endl;
q.Push(6);
q.Push(7);
q.Push(8);
q.Push(9);
q.Push(10);
q.Push(11);
cout << "队头元素为:>" << q.Front() << endl;
cout << "队尾元素为:>" << q.Back() << endl;
cout << "队列的元素个数为:>" << q.Size() << endl;
q.Pop();
cout << "队头元素为:>" << q.Front() << endl;
cout << "队尾元素为:>" << q.Back() << endl;
cout << "队列的元素个数为:>" << q.Size() << endl;
}
int main()
{
TestQueue();
system("pause");
return 0;
}
一、什么是链式队列?
链式队列是队列的一种,与顺序队列的存储方式不一样,跟线性表的单链表一样,只是说只能从头出从尾进而已
二、链式队列的插入和删除
1.插入
起始时,标记队头的_pHead和标记队尾的_pTail都指向队头,随着元素的插入,_pTail 会向后移动,_pHead在插入元素的操作中不会移动
如果队列为空,那就把数据插入到_pHead和_pTail指向的位置,
如果队列不为空,把数据插入到_pTail的下一个位置,再把_pTail向后移动一个位置,即就是_pTail指向的是队列中的最后一个元素
void Push(const T&data)//插入元素,相当于单链表的尾插
{
if (NULL == _pHead)
_pHead = _pTail = new Node(data);
else
{
_pTail->_pNext = new Node(data);
_pTail = _pTail->_pNext;
}
//一定要注意区分if...else...和单独的if
}
时间复杂度:O(1)
2.删除
在删除元素时,只需要把标记队头的_pHead向后移动一个位置就好,标记队尾的_pTail不会移动
但是在删除元素时,需要分为三种情况:
情况一:队列为空队列
此时直接返回
情况二:队列中只有一个元素
此时表明标记队头的_pHead和标记队尾的_pTail指向同一位置
把这两个指针删除即可
情况三:队列中有多个元素
第一步:标记队头元素
第二步:队头指针向后移动一个位置
第三步:删除队头元素
时间复杂度:O(1)
void Pop()//删除元素,相当于单链表的头删
{
if (NULL == _pHead)//处理空队列
return;
else if (_pHead == _pTail)//处理队列中只有一个元素
{
delete _pHead;
//delete _pTail;
_pHead = _pTail = NULL;
}
else
{
pNode pDel = _pHead;//保存需要删除的结点
_pHead = _pHead->_pNext;//更新队头
delete pDel;//删除结点
pDel = NULL;
}
}
源代码
#include
using namespace std;
template<class T>
struct Node
{
Node(const T&data)
:_data(data)
, _pNext(NULL)
{}
Node* _pNext;//结点的指向
T _data;//结点的数据部分
};
template<class T>
class Queue
{
typedef Node Node;
typedef Node* pNode;
public:
Queue()
:_pHead(NULL)
, _pTail(NULL)
{}
void Push(const T&data)//插入元素,相当于单链表的尾插
{
if (NULL == _pHead)
_pHead = _pTail = new Node(data);
else
{
_pTail->_pNext = new Node(data);
_pTail = _pTail->_pNext;
}
//一定要注意区分if...else...和单独的if
}
void Pop()//删除元素,相当于单链表的头删
{
if (NULL == _pHead)//处理空队列
return;
else if (_pHead == _pTail)//处理队列中只有一个元素
{
delete _pHead;
//delete _pTail;
_pHead = _pTail = NULL;
}
else
{
pNode pDel = _pHead;
_pHead = _pHead->_pNext;
delete pDel;
pDel = NULL;
}
}
T&Front()//获得队头元素
{
return _pHead->_data;
}
const T&Front()const
{
return _pHead->_data;
}
T&Back()//或者队尾元素
{
return _pTail->_data;
}
const T&Back()const
{
return _pTail->_data;
}
bool Empty()const//判断队列是否为空
{
return NULL = _pHead;
}
size_t Size()//获得队列的元素个数
{
pNode pCur = _pHead;
int count = 0;
while (pCur)//注意_pTail标记的是最后一个元素
{
count++;
pCur = pCur->_pNext;
}
return count;
}
private:
pNode _pHead;//标记队头
pNode _pTail;//标记队尾
};
void TestQueue()
{
Queue<int> q;
q.Push(1);
q.Push(2);
q.Push(3);
q.Push(4);
q.Push(5);
cout << "队列中的元素个数为:> " << q.Size() << endl;
cout << "队头为:> " << q.Front() << endl;
cout << "队尾为:> " << q.Back() << endl;
q.Pop();
q.Pop();
q.Pop();
cout << "队列中的元素个数为:> " << q.Size() << endl;
cout << "队头为:> " << q.Front() << endl;
cout << "队尾为:> " << q.Back() << endl;
q.Push(6);
q.Push(7);
q.Push(8);
q.Push(9);
cout << "队列中的元素个数为:> " << q.Size() << endl;
cout << "队头为:> " << q.Front() << endl;
cout << "队尾为:> " << q.Back() << endl;
q.Pop();
q.Pop();
q.Pop();
q.Pop();
q.Pop();
cout << "队列中的元素个数为:> " << q.Size() << endl;
cout << "队头为:> " << q.Front() << endl;
cout << "队尾为:> " << q.Back() << endl;
}
int main()
{
TestQueue();
system("pause");
return 0;
}
一、什么是优先级队列
优先级队列也是一种队列,只是队列中的元素有了优先级,在删除元素时,优先级高的元素先出队列,优先级队列有两种形式,分别是顺序优先级队列和链式优先级队列
二、优先级队列的插入和删除
方式一:
插入:乱序的插入,此时插入操作的时间复杂度为O(1)
删除:因为是优先级队列,所以在删除元素时,优先级高的需要先出队列,故需要遍历整个队列,找到优先级高的元素,让其先出队列,故时间复杂度为O(N)
方式二:
插入:有序的插入,按优先级的高低,由高到低依次插入,故时间复杂度为O(N)
删除:因为在插入元素时是根据优先级的高低来插入的,所以在删除时,直接从队头删除即可,故时间复杂度为O(1)
ps:优先级队列的底层数据结构是堆