队列详解!

与栈一样,队列也是一种限定性的数据结构,即限定其插入操作只能在线性表的一端进行,而访问和删除操作只能在线性表的另一端进行。

限定只能在线性表的一端进行插入操作,在另一端进行访问和删除操作的线性表称为队列,将进行插入操作(入队)的一端称为队尾,可以进行访问和删除操作(出对)的一端称为队头。队列也称为先进先出(FIFO)线性表。

链队列

用带头结点的链表表示的队列称为链队列,在链队列中,队头为链表的第一个元素,队尾为链表的最后一个元素,因此出队操作在队头进行,而入队操作在队尾进行。

链队列的front为链表的头结点,队头为front的下一个结点,方便出队;rear和队尾都是链表的最后一个节点,方便入队。

#include
using namespace std;
typedef int datatype;
constexpr auto N = 10;

typedef struct clNode {
	datatype data;
	clNode* next;
}*cqNode;

//链队列
struct chainQueue {
	cqNode front; //头结点
	cqNode rear; //队尾
	chainQueue() :front(NULL), rear(NULL) {
	}
};

//判断队列cp是否为空,如果为空,返回true,否则返回false
bool empty(chainQueue cq) {
	return cq.front == NULL;
}

//入队:在链队列cq的队尾添加一个元素
void push(chainQueue& cq, datatype x) {
	cqNode tmp = new clNode;
	tmp->data = x;
	tmp->next = NULL;
	if (empty(cq)) { 
		cq.front = new clNode;
		cq.front->next = tmp;  //链队列中有一个新加入的元素
	}else {
		cq.rear->next = tmp; //新加入的结点当前链队列队尾的后面
	}
	cq.rear = tmp;  //队尾始终为新加入的结点
}

//获取链队列cq队头元素的值
datatype front(chainQueue cq) {
	if (empty(cq)) {
		cout << "队列为空!" << endl;
		return -1;
	}
	return cq.front->next->data;
}

//出队:删除链队列cq的队头
void pop(chainQueue& cq) {
	if (empty(cq)) {
		cout << "队列为空!" << endl;
		return;
	}
	cqNode tmp = cq.front->next; //队头结点,将被删除
	cq.front->next = tmp->next;
	delete tmp; tmp = NULL;
	if (cq.front->next == NULL) {  //将链队列中的元素都删除
		delete cq.front;
		cq.front = NULL;
		cq.rear = NULL;
	}
}

//遍历cq的所有元素,同时清空链队列cq
void cq_traverse(chainQueue& cq) {
	while (!empty(cq)) {
		cout << front(cq) << " ";
		pop(cq);
	}
}

int main()
{
	chainQueue cq1;
	push(cq1, 5);
	push(cq1, 34);
	push(cq1, 55);
	push(cq1, 56);
	cq_traverse(cq1);
}

循环队列

循环队列的实现基于顺序表,队头为顺序表的第一个有效元素,队尾为顺序表的最后一个元素。其将表示队列的数组看成一个环,即下标最大的元素的下一个元素的下标为0,这样可以避免假溢出现象。

假溢出现象:假设下标0和1的位置空闲(已出队),rear以达到最大,即此时的数组sq并未填满,成为“假溢出”。

#include
using namespace std;
typedef int datatype;
constexpr auto N = 6;

//对于队列sq入队操作,需要先将sq.rear向后移一位,而出队操作需要将sq.front向后移一位
//但是当他们的值都为N时,需要调整为0,简化操作,设置以下宏定义
#define next(x) ((x)==N-1?0:(x)+1)

//循环队列的类型定义
typedef struct sqNode {
	datatype data[N];
	int front;
	int rear;
	sqNode() :front(0), rear(0) {
		memset(data, 0, sizeof(data));
	}
}seqQueue;

//判断队列sq是否为空
//当队列为空时,front = rear = 0,每当插入元素尾指针+1,删除元素是头指针-1
//为了达到区别队空or队满,可以通过牺牲一个存储空间来实现
bool empty(seqQueue sq) {
	return sq.rear == sq.front; 
}

//判断队列sq是否为满
bool full(seqQueue sq) {
	return next(sq.rear) == sq.front; 
}

//入队:在队列sq的队尾添加一个元素,元素的值为x
void push(seqQueue& sq, datatype x) {
	if (full(sq)) {
		cout << "队列已满,入队失败!" << endl;
		return;
	}
	sq.data[sq.rear] = x;
	sq.rear = next(sq.rear);
}

//获取队列sq的队头元素,作为函数的返回值
datatype front(seqQueue sq) {
	if (empty(sq)) {
		cout << "队列为空,获取队头元素失败" << endl;
		return -1;
	}
	return sq.data[sq.front];
}

//出队:删除sq的队头元素
void pop(seqQueue& sq) {
	if (empty(sq)) {
		cout << "队列为空,出队失败!" << endl;
		return;
	}
	sq.front = next(sq.front);
}

//遍历sq,并清空sq
void sq_traverse(seqQueue& sq) {
	while (!empty(sq)) {
		cout << front(sq) << " ";
		pop(sq);
	}
}

int main()
{
	seqQueue sq1;
	push(sq1, 5);
	push(sq1, 55);
	push(sq1, 89);
	push(sq1, 3);
	push(sq1, 26);
	
	/*sq_traverse(sq1);
	cout << endl;
	sq_traverse(sq1);*/
	front(sq1);
	//pop(sq1);
	//sq_traverse(sq1);
	//push(sq1, 99);
	sq_traverse(sq1);
}

 

你可能感兴趣的:(数据结构与算法,--书籍笔记,数据结构,链表)