1.本节学习要点:
2.基本定义与相关名词:
队列:限定一端插入、另一端删除的线性表。
空队:队列中没有元素时
队头(front):允许删除的一端
队尾(rear) :允许插入的一端
PS:在队列中依次加入元素a1,a2,…,an之后,a1是队头元素,an是队尾元素。退出队列的次序只能是a1,a2,…,an。
4.基本运算:
2) 判队空EMPTY(Q):若队列Q空,返回1,否则返回0。
3) 入队ENQUEUE(Q, x):在队尾插入元素x,x 成为新的队尾
4) 出队DEQUEUE (Q):将队头元素删除,并返 回该元素。
5) 取队头GETHEAD(Q):取队头元素,但不 删除它。
5.顺序队列:
顺序队列:队列的顺序存储结构。
1.用数组来实现。
2. 队头、队尾都变化,需要两个指针分别指示队头和队尾当前位置,front:队头指针,rear:队尾指针。
3. 约定:队头指针指向队头前一位置,尾指针指向队尾。
PS:
1.当队满时再入队必定产生空间溢出,简称“上溢”;上溢是一种出错状态,应该设法避免。
2.当队空时再出队也将产生溢出,简称“下溢”;下溢则可能是正常现象,因为队列初态或终态都是空,下溢常作程序控制转移的条件。
3.队列并不满,若尾指针已到数组上界,再入队也会溢出,称为“假上溢”。
4.假溢出:当元素被插入到数组中下标最大的位置上之后,队列的空间就用尽了,尽管此时数组的低端还有空闲空间,这种现象叫做假溢出。
5.出队后原队头元素仍存在,但不作有效队元素,可直接覆盖。
1.循环队列(解决假溢出):
循环队列:将存储队列的数组头尾相接;设想向量空间是一个首尾相接的圆环,称为循环向量,其中的队列称为循环队列(Circular Queue)。
1.空队时:front=rear;
2.入队时尾指针向前追赶头指针:
3.出队时头指针向前追赶尾指针,
4.队满时:front=rear。
如何判断队满队空?方法:
2)另设标志位flag以区分队空、队满:若入队后出现rear=front,则置flag=1,表明队满;其他情况flag=0.
3)空置一单元法:当循环响亮中只剩下1个元素空间,即已经存放maxsiza-1个元素时,就认为队满。
即队满条件为: ront=(rear+1)%maxsize; 队空条件为: rear=front
此时少用一个存储单元,且front所指处总为空。
2.基本运算(循环队列,方法(3)判队空队满):
typedef int datatype; //队列元素类型,假设为整型
const int maxsize=100; //队列容量
typedef struct {
datatype data[maxsize];
int front,rear;
} sqqueue;
//初始化
void init_sqqueue(sqqueue *sq) {
sq−>front=sq−>rear=0; //不能为-1,0~maxsize-1
}
//判空
int empty_sqqueue(sqqueue *sq) {
if(sq−>rear==sq−>front) return 1;
else return 0;
}
//取队列
int gethead_sqqueue(sqqueue *sq,datatype *x) {
if(sq−>rear==sq−>front){
cout<<”队空,无队头可取!\n”;return 0;
}else //头指针的下一个位置才是队头
{*x=sq−>data[(sq−>front+1)%maxsize];
return 1;}
}
//入队
int en_sqqueue(sqqueue *sq,datatype x) {
if((sq−>rear+1)%maxsize==sq−>front)
{prinf(”队满,不能入队!\n”);return 0;} //上溢
else
{sq−>rear=(sq−>rear+1)%maxsize;
sq−>data[sq−>rear]=x;
return 1;}
}
//出队
int de_sqqueue(sqqueue *sq,datatype *x) {
if(sq−>rear==sq−>front)
{cout<<”队空,不能出队!\n”;return 0;} //下溢
else
{sq−>front=(sq−>front+1)%maxsize;
*x=sq−>data[sq−>front];
return 1;} }
5.链队列:
链队列:队列的链式存储结构,运算受限的单链表。
1.尾插法,取链表的头部作队头,尾部作队尾。
2.队列没有元素移动问题,链式存储是为了动态利用存储空间。
3.为运算方便,增加头结点。
typedef struct node * pointer; //结点指针类型
struct node { //链队列结点结构
datatype data;
pointer next;
};
typedef struct {
pointer front,rear;
} lkqueue;
//初始化
void init_lkqueue(lkqueue *lq) {
pointer p;
p=new node; //申请头结点空间
p−>next=NULL; //头结点next指针为空
lq−>front=p; //头指针指向头结点
lq−>rear=p; //尾指针也指向头结点
}
//判空
int empty_lkqueue(lkqueue *lq) {
if(lq−>rear==lq−>front) return 1;
else return 0;
}
//取队头
int gethead_lkqueue(lkqueue *lq,datatype *x) {
pointer p;
if(lq−>rear==lq−>front)
{cout<<”队空,无队头可取!\n”;return 0;}
else
{p=lq−>front−>next; //front是头结点,
//front−>next才是队头
*x=p−>data; //取出队头元素值
return 1;
}
}
//入队
void en_lkqueue(lkqueue *lq,datatype x) {
pointer p;
p=new node; //申请新结点空间
p−>data=x; //给新结点赋值
lq−>rear−>next=p; //原尾指针指向新结点
lq−>rear=p; //新结点成为新尾结点
p−>next=NULL; //新尾结点next指针为空
}
//出队
int de_lkqueue(lkqueue *lq,datatype *x) {
pointer s;
if(lq−>rear==lq−>front)
{cout<<”队空,不能出队!\n”;return 0;} //下溢
else
{s=lq−>front−>next;
*x=s−>data;
lq−>front−>next=s−>next;
if(s−>next==NULL)
lq−>rear=lq−>front;//尾指针也要修改
delete s; return 1;}
}
//等效出队:
int de_lkqueue2(lkqueue *lq,datatype *x) {
pointer s;
if(lq−>rear==lq−>front)
{cout<<”队空,不能出队!\n”;return 0;} //下溢
else
{s=lq−>front;
*x=s−>next−>data;
lq−>front=s−>next;
delete s; return 1;}
}
6.顺序队列与链表的比较:
时间性能:
循环队列和链队列的基本操作都需要常数时间O (1)。
空间性能:
循环队列:必须预先确定一个固定的长度,所以有存储元素个数的限制和空间浪费的问题。
链队列:没有队列满的问题,只有当内存没有可用空间时才会出现队列满,但是每个元素都需要一个指针域,从而产生了结构性开销。
如果有错误请大家指出,共同学习共同进步!更多百度百科