栈与队列是什么
栈和队列其实就是操作受限制的线性表。
下面来复习一下线性表的概念
具有n个相同类型元素的有限序列
有的人就会问,那么它们受限在哪里呢?
栈:只允许一段插入和删除。
队列:只允许一端插入一端删除。
栈
前面说了栈是一种受限的线性表,因为它只允许插入和删除操作都在一端进行。
栈的专业术语
栈顶:即允许插入和删除的一端
栈底:即不允许删除和插入的一端
空栈:栈中不包含任何元素
LIFO后进先出 or FILO先进后出
例子:弹夹、烤串
栈的基本操作
初始化空栈栈
判断是否为空栈
出栈
入栈
读取栈顶元素
顺序栈的实现
顺序栈其实就是顺序表的简化版,它只可以在一端插入和删除。代码和顺序表其实大致相同
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<stdlib.h>
#define Maxsize 50
typedef int Elemtype;
typedef struct {
Elemtype data[Maxsize];
int top;
}sqStack;
//初始化栈
void InitStack(sqStack& s) {
s.top = -1;
}
//判断栈是否为空(因为只是读取栈里面的元素,所以不用&)
bool IsEmpty(sqStack s) {
if (s.top == -1) {
return false;
}
return true;
}
//判断栈是否为满
bool IsFull(sqStack s) {
if (s.top == Maxsize - 1) {
return false;
}
return true;
}
//入栈
bool Push(sqStack& s,Elemtype x) {
//判断栈是否为满
if (IsFull(s)) {
//x是要插入的元素
s.data[++s.top] = x;
}
return true;
}
//出栈(因为x的需要改变,所以要加&)
bool Pop(sqStack& s,int &x) {
//s是栈,x是出栈的元素
//判断栈是否为空
if (IsEmpty(s)) {
//x是出栈的元素
x = s.data[s.top--];
}
return true;
}
//输出栈
void printfStack(sqStack& s) {
for (int i = 0; i <= s.top; i++) {
printf("%d", s.data[i]);
}
printf("\n");
}
//读取栈顶元素
int top(sqStack s,int& x) {
//判断栈是否为满
if (IsEmpty(s)) {
//x是出栈的元素
x = s.data[s.top];
}
return x;
}
int main() {
sqStack s;
InitStack(s);
Elemtype x;
scanf("%d", &x);
while (x != 9999) {
Push(s, x);
scanf("%d", &x);
}
printfStack(s);
int p;
Pop(s,p);
printfStack(s);
printf("出栈元素为:%d\n",p);
printf("栈顶元素为:%d\n", top(s,p));
}
栈的链式实现
其实栈的链式存储其实就是只有头插法的链表,不过在删除上面只可以删除链表的表头指向的下一个元素
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<stdlib.h>
typedef int ElemType;
//定义链表的结点,链表中逻辑相邻的两个元素物理位置不相邻
typedef struct LNode {
ElemType data;//数据域
struct LNode* next;//指针域
}LNode, * LinkList;//别名
//头插法,即从头结点后面插入。比如输入的是12345,插入之后可能顺序就是54321
//也就是进栈的方式
LinkList creatList_Head(LinkList& L) {
LNode* s;//定义要插入的指针
int x;//定义需要插入的数据域
L = (LinkList)malloc(sizeof(LNode));//头结点,没有数据域(默认值)。用于表明这是指针
L->next = NULL;//因为头结点的指向下一个结点,但是还没定义所以是NULL
scanf("%d", &x);
//使用9999作为中止循环的结束符
while (x != 9999) {
s = (LinkList)malloc(sizeof(LNode));//申请空间创建新的结点
s->data = x;//把数据赋值给插入结点的数据域
s->next = L->next;//把指针指向的下一个结点赋给插入的结点的指针(头插法)
L->next = s;//然后再把指针指向的结点指向插入的结点
scanf("%d", &x);//再次读取数据知道x等于9999为止
}
return L;
}
//入栈操作(跟头插法一样)
bool Push(LinkList L,ElemType x){
//因为链表的结点可以不断的加,所以这里没有限制条件
LinkList s = (LinkList)malloc(sizeof(LNode));
s->data = x;
s->next = L->next;
L->next = s;
return true;
}
//出栈操作
bool Pop(LinkList L, int& x) {
//获取头结点的下一个位置只在这里操作
LinkList s = L->next;
//判断是否为空栈
if (s != NULL) {
x = s->data;
L->next = s->next;
free(s);
s->next = NULL;
}
return true;
}
void PrintfStack(LinkList L) {
LinkList s;
s = L->next;
while (s!= NULL) {
printf("%d", s->data);
s = s->next;
}
printf("\n");
}
void GetTop(LinkList L) {
LinkList top = L->next;
printf("栈顶元素为:%d\n", top->data);
}
int main() {
LinkList St;
creatList_Head(St);
//入栈
printf("入栈\n");
Push(St, 5);
printf("入栈后的栈:");
PrintfStack(St);
//出栈
printf("出栈\n");
int x;
Pop(St, x);
printf("出栈元素为:%d\n", x);
printf("出栈后的栈:");
PrintfStack(St);
//获取栈顶元素
GetTop(St);
}
队列
队列也是一种受限的线性表,它只允许一端进行插入操作、另一端进行删除操作。
队列的专业术语
队头:允许删除的一端
队尾:允许插入的一端
空队列:队列中无任何元素
FIFO先进先出 or LILO后进后出
例子:告诉公路的收费站、饭堂排队
队列的操作
初始化队列
判断是否为空队列
出队
入队
读取队头元素