栈与队列详解

数组与链表是数据存储的基本方法。成员访问中,数组可以随机的访问成员,链表通过遍历可以去找到需要的数据单元。

栈与队列是两种特殊的数据成员管理方式。它们本身的数据存放方式也是数组或者链表。

  • 栈:FILO(先进后出)。只允许在栈顶添加元素和删除元素(出栈和入栈)
  • 队列:FIFO(先进先出)。在队首删除元素,在队尾添加元素(出队和入队)

1. 栈

栈顶、栈底:允许进行插入和删除操作的一端称为栈顶(top),另一端称为栈底(bottom),栈底固定,栈顶浮动;

空栈:栈中元素个数为零时称为空栈;

入栈:插入一般称为入栈(PUSH);

出栈:删除一般称为出栈(POP);

1.1 顺序栈

顺序栈,即用顺序表(底层实现是数组)实现栈存储结构,例如我们先使用顺序栈(a数组)存储{1,2,3,4},存储状态如下图所示:

于是,我们可以得到顺序栈的存储结构如下:

const int MAXSIZE = 10;
// 顺序栈的存储结构
typedef struct sq_stack{
	int data[MAXSIZE]; // 栈大小
	int top = -1; // top指向栈顶
}SqStack;

栈空:很自然的我们将top初始化为-1,代表栈空的状态top==-1;

栈满:因为MAXSIZE代表最大存储个数,所哟我们top最大下标只能到MAXSIZE-1,所以当top==MAXSIZE-1时即为栈满的状态;

1.1.1 入栈

首先我们需要明确,因为栈限制再栈顶进行插入和删除,所以我们只需对top进行移动即可实现。对于入栈(栈不满的情况下),设想一下,我们应该先得到空出位置的编号,再将其对号入座,而top是栈顶元素的下标,所以我们先将top++,再将数组对应的top位置赋值。

// 入栈
void Push(SqStack* s, int a)
{
	if (s->top == MAXSIZE - 1) {
		cout << "栈满" << endl;
		return;
	} else {
		s->top++;
		s->data[s->top] = a;
	}
}

1.1.2 出栈

同样的,出栈我们应该先得到栈顶元素的数据,然后将其栈顶位置下标减1即可。(实际上原来top下标的数据仍然在存在于数组中,但是不在栈中,因为它不在top范围内)。

// 出栈
int Pop(SqStack* s)
{
	int val = 0;
	if (s->top == -1) {
		cout << "栈空" << endl;
		return -1;
	} else {
		val = s->data[s->top];
		s->top--;
	}
	return val;
}

1.1.3 顺序栈完整代码

#include<iostream>
using namespace std;

const int MAXSIZE = 10;
// 栈存储结构
typedef struct sq_stack{
	int data[MAXSIZE]; // 栈大小
	int top = -1; // top指向栈顶
}SqStack;

// 入栈
void Push(SqStack* s, int a)
{
	if (s->top == MAXSIZE - 1) {
		cout << "栈满" << endl;
		return;
	} else {
		s->top++;
		s->data[s->top] = a;
	}
}

// 出栈
int Pop(SqStack* s)
{
	int val = 0;
	if (s->top == -1) {
		cout << "栈空" << endl;
		return -1;
	} else {
		val = s->data[s->top];
		s->top--;
	}
	return val;
}

// 打印
void Print(SqStack* s)
{
	if (s->top == -1) {
		cout << "栈空" << endl;
	}
	while (s->top != -1) {
		cout << Pop(s) << endl;;
	}
}


int main()
{
	SqStack sq;
	SqStack* s = &sq;
	Push(s, 2);
	Push(s, 4);
	Push(s, 5);
	Push(s, 8);
	Pop(s);
	Print(s);
}

2. 队列 

队列是一种特殊的线性表,特殊之处在于它只允许在表的前端(front)进行删除操作,而在表的后端(rear)进行插入操作。

2.1 顺序队列

顺序队列通常采用一维数组存储队列中的元素,另外增加两个索引分别指示数组中存放的队首元素和队尾元素。其中指向队首的索引为front,指向队尾的索引为rear。

// 顺序队列的存储结构
typedef struct sq_queue {
	int data[MAXSIZE];
	int front = 0;
	int rear = 0;
}SqQueue;

队列空:当front==rear时代表队列空;

队列满:当rear>MAXSIZE-1时代表队列满;

2.1.1 入队

void Push(SqQueue* s, int a)
{
	if (s->rear <= MAXSIZE - 1) {
		s->data[s->rear] = a;
		s->rear++;
	} else {
		cout << "队满" << endl;
	}
}

2.1.2 出队

int Pop(SqQueue* s)
{
	if (s->front == s->rear) {
		cout << "队空" << endl;
		return -1;
	} else {
		return s->data[s->front++];
	}
}

2.1.3 顺序队列完整代码

typedef struct sq_queue {
	int data[MAXSIZE];
	int front = 0;
	int rear = 0;
}SqQueue;

void Push(SqQueue* s, int a)
{
	if (s->rear <= MAXSIZE - 1) {
		s->data[s->rear] = a;
		s->rear++;
	} else {
		cout << "队满" << endl;
	}
}

int Pop(SqQueue* s)
{
	if (s->front == s->rear) {
		cout << "队空" << endl;
		return -1;
	} else {
		return s->data[s->front++];
	}
}

void Print(SqQueue* s)
{
	while (s->front != s->rear) {
		cout << s->data[s->front] << endl;
		s->front++;
	}
}


int main()
{
	SqQueue sq;
	SqQueue* s = &sq;
	Push(s, 2);
	Push(s, 4);
	Push(s, 5);
	Push(s, 8);
	Pop(s);
	Print(s);
}

2.2 循环队列

假溢出:但是按照上述顺序存储的方式,很容易出现假溢出。所谓假溢出就是经过多次插入和删除操作后,实际队列还有存储空间,但是又无法向队列中插入元素,简单来说就是数组越界的错误。

例如在队列中删除a和b后,然后以此插入i和j,当插入j后就会出现队尾索引rear越出数组下界造成假溢出,如下图:

 解决假溢出的办法就是后面满了,再从头开始,也就是头尾相连的循环结构体。

我们把队列的这种头尾相连的顺序存储结构称为循环队列。

当队尾rear和队首front到达存储空间的最大值时,让rear或front转化为0,这样就可以将元素插入到队列的空闲存储单元中,有效的利用存储空间,消除假溢出。

其实循环队列也是用数组存储,只不过为了形象表现出来,我们将图做成一个“环状”,实际上还是线性的数组结构。

// 循环队列的存储结构
typedef struct sq_queue {
	int data[MAXSIZE];
	int front = 0;
	int rear = 0;
}SqQueue;

队空:显然当front==rear时表示没有元素,此时队空。 

队满:为了和队空区分,我们不妨让队列空出一个位置,这个位置不妨任何元素,仅仅是为了区别队空和队满,但也可能整整相差一圈,所以如果最大尺寸为MAXSIZE,那么当(rear+1)%MAXSIZE==front时就代表队列满了。

 获取队列长度:

rear>front,队列长度为rear-front

rear<front,队列长度为MAXSIZE-[(front-rear)+1]+1=rear-front+MAXSIZE

所以可以统一为(rear-front+MAXSIZE)%MAXSIZE

int Length(SqQueue* s)
{
	return (s->rear - s->front + MAXSIZE) % MAXSIZE;
}

2.2.1 入队

void Push(SqQueue* s, int a)
{
	if ((s->rear + 1) % MAXSIZE == s->front) {
		cout << "栈满" << a << endl;
		return;
	} else {
		s->data[s->rear] = a;
		s->rear = (s->rear + 1) % MAXSIZE;
	}
}

2.2.2 出队

int Pop(SqQueue* s)
{
	if (s->front == s->rear) {
		cout << "队空" << endl;
		return -1;
	} else {
		int val = s->data[s->front];
		s->front = (s->front + 1) % MAXSIZE;
		return val;
	}
}

2.2.3 循环队列完整代码

#include<iostream>
using namespace std;
const int MAXSIZE = 10;

typedef struct sq_queue {
	int data[MAXSIZE];
	int front = 0;
	int rear = 0;
}SqQueue;

void Push(SqQueue* s, int a)
{
	if ((s->rear + 1) % MAXSIZE == s->front) {
		cout << "栈满" << a << endl;
		return;
	} else {
		s->data[s->rear] = a;
		s->rear = (s->rear + 1) % MAXSIZE;
	}
}

int Pop(SqQueue* s)
{
	if (s->front == s->rear) {
		cout << "队空" << endl;
		return -1;
	} else {
		int val = s->data[s->front];
		s->front = (s->front + 1) % MAXSIZE;
		return val;
	}
}

void Print(SqQueue* s)
{
	while (s->front != s->rear) {
		cout << s->data[s->front] << endl;
		s->front = (s->front + 1) % MAXSIZE;
	}
}

int Length(SqQueue* s)
{
	return (s->rear - s->front + MAXSIZE) % MAXSIZE;
}


int main()
{
	SqQueue sq;
	SqQueue* s = &sq;
	Push(s, 2);
	Push(s, 4);
	Push(s, 5);
	Push(s, 8);
	Pop(s);
	Push(s, 2);
	Push(s, 4);
	Push(s, 5);
	Push(s, 8);
	Push(s, 2);
	Push(s, 4);
	Pop(s);
	Push(s, 2);
	Print(s);
}

 

《数据结构C语言版》——栈和队列详解(图文并茂),从零开始的学习

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

东城青年

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值