栈的原理精讲
栈是一种线性结构,即线性排列。
好比如一条胡同:
先进来的车子只能等后面进来的车子出去后,他才能出去,也就体现了栈的特性:“后进先出”!
栈也是一种线性表,只不过它是操作受限的线性表,只能在一端操作。
进出的一端称为栈顶(top),另一端称为栈底(base)。栈可以用顺序存储,也可以用链式存储。我们先看顺序存储方式:
其中,base 指向栈底,top 指向栈顶。
注意:栈只能在一端操作,后进先出,这是栈的关键特征,也就是说不允许在中间查找、取值、插入、删除等操作,我们掌握好顺序栈的初始化、入栈,出栈,取栈顶元素等操作即可。
栈顶永远都是指向下一个待插入元素的位置!!!
顺序栈的算法实现
代码中所用到的所有接口:
bool initStack(Stack &stack); // 初始化栈
bool estimateStackEmpty(Stack &stack); // 判断是否为空
bool estimateStackFull(Stack &stack); // 判断是否已满
int stackSize(Stack &stack); // 获取栈已存储的元素个数
bool insertStack(Stack &stack, ElemType value); // 入栈
bool popStack(Stack &stack, ElemType &vlaue); // 出栈
bool gainStack(Stack &stack, ElemType& value); // 获取栈顶的元素
void deleteStack(Stack &stack); // 释放栈的内存
栈数据结构的定义
#define StackMax 128 // 栈最大可以存放的元素个数
typedef int ElemType; // 栈存储的数据类型
// 建栈
typedef struct _SqStack {
ElemType *top; // 栈顶指针
ElemType *base; // 栈底指针
}Stack;
栈的初始化
为栈底base分配内存,然后top,base都指向这块内存!
bool initStack(Stack &stack) {
stack.base = new ElemType[StackMax]; // 分配内存(栈底指向它)
if (!stack.base) { // 判断是否分配内存失败
return false;
}
stack.top = stack.base; // 栈顶栈底指向同一个位置
return true;
}
检测栈是否为空 && 检测栈是否已满
当栈顶与栈尾相等时,即栈为空!(由上面栈的初始化可知)
bool estimateStackEmpty(Stack &stack) {
if (stack.top == stack.base) { // 当栈顶栈底指向同一个位置,栈为空。
return true;
}
return false;
}
当栈顶减去栈尾等于宏定义StackMax时,即栈已满!
bool estimateStackFull(Stack &stack) { // 指针的特性:指针一减去指针二,可以得出他们
if ((stack.top - stack.base) == StackMax) { // 之中相隔几个元素。
return true; // 当与栈所能存储的最大值相等时,为满。
}
return false;
}
入栈
入栈操作:判断是否栈满,如果栈已满,则入栈失败,否则将元素放入栈顶,栈顶指针向上移动一个空间(top++)。
栈顶永远都是指向下一个待插入元素的位置!!!
bool insertStack(Stack &stack, ElemType value) {
if (estimateStackFull(stack)) {
cout << "栈已满!" << endl;
return false;
}
*stack.top = value; // 栈顶指针解引用后,value值赋值给它存储
stack.top++; // 栈顶指针只增一
return true;
}
出栈
出栈操作: 和入栈相反,出栈前要判断是否栈空,如果栈是空的,则出栈失败,否则将栈顶自减一后(top–),减一后的栈顶指向的内存中的元素暂存给一个变量返回。
// 栈顶元素出栈后,保存出栈元素返回
bool popStack(Stack &stack, ElemType &value) {
if (estimateStackEmpty(stack)) {
cout << "栈为空!" << endl;
return false;
}
stack.top--; // 栈顶指针自减一
value = *stack.top; // 将栈顶指针所在位置的值赋值给value返回
return true;
}
获取栈顶元素
取栈顶元素和出栈不同,取栈顶元素只是把栈顶元素复制一份,栈的元素个数不变,而出栈是指栈顶元素取出,栈内不再包含这个元素。
bool gainStack(Stack &stack, ElemType& value) {
if (estimateStackEmpty(stack)) {
cout << "栈为空!" << endl;
return false;
}
value = *(stack.top - 1); // 将栈顶指针“假”减一后的值赋值给value返回
return true;
}
清空栈
释放内存
void deleteStack(Stack &stack) {
if (stack.base) {
delete stack.base;
stack.top = NULL;
stack.base = NULL;
cout << "栈已释放内存!" << endl;
}
}
获取栈存储的元素个数
栈顶减去栈尾即为栈存储的元素个数!
int stackSize(Stack &stack) { // 指针的特性:指针一减去指针二,可以得出他们之中相隔几个元素。
return stack.top - stack.base;
}
完整源码实现
#include <iostream>
#include <Windows.h>
using namespace std;
#define StackMax 128 // 栈最大可以存放的元素个数
typedef int ElemType; // 栈存储的数据类型
// 建栈
typedef struct _SqStack {
ElemType *top; // 栈顶指针
ElemType *base; // 栈底指针
}Stack;
bool initStack(Stack &stack); // 初始化栈
bool estimateStackEmpty(Stack &stack); // 判断是否为空
bool estimateStackFull(Stack &stack); // 判断是否已满
int stackSize(Stack &stack); // 获取栈已存储的元素个数
bool insertStack(Stack &stack, ElemType value); // 入栈
bool popStack(Stack &stack, ElemType &vlaue); // 出栈
bool gainStack(Stack &stack, ElemType& value); // 获取栈顶的元素
void deleteStack(Stack &stack); // 释放栈的内存
bool initStack(Stack &stack) {
stack.base = new ElemType[StackMax]; // 分配内存(栈底指向它)
if (!stack.base) { // 判断是否分配内存失败
return false;
}
stack.top = stack.base; // 栈顶栈底指向同一个位置
return true;
}
bool estimateStackEmpty(Stack &stack) {
if (stack.top == stack.base) { // 当栈顶栈底指向同一个位置,栈为空。
return true;
}
return false;
}
bool estimateStackFull(Stack &stack) { // 指针的特性:指针一减去指针二,可以得出他们
if ((stack.top - stack.base) == StackMax) { // 之中相隔几个元素。
return true; // 当与栈所能存储的最大值相等时,为满。
}
return false;
}
int stackSize(Stack &stack) { // 指针的特性:指针一减去指针二,可以得出他们之中相隔几个元素。
return stack.top - stack.base;
}
bool insertStack(Stack &stack, ElemType value) {
if (estimateStackFull(stack)) {
cout << "栈已满!" << endl;
return false;
}
*stack.top = value; // 栈顶指针解引用后,value值赋值给它存储
stack.top++; // 栈顶指针只增一
return true;
}
// 栈顶元素出栈后,保存出栈元素返回
bool popStack(Stack &stack, ElemType &value) {
if (estimateStackEmpty(stack)) {
cout << "栈为空!" << endl;
return false;
}
stack.top--; // 栈顶指针自减一
value = *stack.top; // 将栈顶指针所在位置的值赋值给value返回
return true;
}
bool gainStack(Stack &stack, ElemType& value) {
if (estimateStackEmpty(stack)) {
cout << "栈为空!" << endl;
return false;
}
value = *(stack.top - 1); // 将栈顶指针“假”减一后的值赋值给value返回
return true;
}
void deleteStack(Stack &stack) {
if (stack.base) {
delete stack.base;
stack.top = NULL;
stack.base = NULL;
cout << "栈已释放内存!" << endl;
}
}
int main(void) {
Stack stack;
// 初始化栈
initStack(stack);
// 插入元素
int n = 0;
int value = 0;
cout << "请输入需要插入的元素个数:";
cin >> n;
while (n > 0) {
cin >> value;
insertStack(stack, value);
n--;
}
// 获取栈顶的元素
gainStack(stack, value);
cout << "当前栈顶的元素是:" << value << endl;
// 获取栈的元素个数
cout << "当前栈的元素个数是:" << stackSize(stack) << endl;
// 出栈
cout << "出栈顺序:" << endl;
while (!estimateStackEmpty(stack)) {
popStack(stack, value);
cout << value << " ";
}
cout << endl;
// 释放栈的内存
deleteStack(stack);
system("pause");
return 0;
}
运行截图:
课后思考:
如果把栈的结构体定义改为如下形式,该如何实现操作栈的算法?
typedef struct _SqStack {
int top; // 栈顶的位置
ElemType* base; // 栈底指针
}Stack;
=
=
=
具体实现:
#include <iostream>
#include <Windows.h>
using namespace std;
#define StackMax 128 // 栈最大可以存放的元素个数
typedef int ElemType; // 栈存储的数据类型
// 建栈
typedef struct _SqStack {
int top; // 栈顶的位置
ElemType* base; // 栈底指针
}Stack;
bool initStack(Stack& stack); // 初始化栈
bool estimateStackEmpty(Stack& stack); // 判断是否为空
bool estimateStackFull(Stack& stack); // 判断是否已满
int stackSize(Stack& stack); // 获取栈已存储的元素个数
bool insertStack(Stack& stack, ElemType value); // 入栈
bool popStack(Stack& stack, ElemType& vlaue); // 出栈
bool gainStack(Stack& stack, ElemType& value); // 获取栈顶的元素
void deleteStack(Stack& stack); // 释放栈的内存
bool initStack(Stack& stack) {
stack.base = new ElemType[StackMax]; // 分配内存(栈底指向它)
if (!stack.base) { // 判断是否分配内存失败
return false;
}
stack.top = 0;
return true;
}
bool estimateStackEmpty(Stack& stack) {
if (stack.top == 0) { // 当栈顶位置等于零,为空
return true;
}
return false;
}
bool estimateStackFull(Stack& stack) {
if (stack.top == StackMax) { // 当栈顶位置等于栈所能存储的最大值时,为满
return true;
}
return false;
}
int stackSize(Stack& stack) {
return stack.top; // 栈顶位置就是元素的个数
}
bool insertStack(Stack& stack, ElemType value) {
if (estimateStackFull(stack)) {
cout << "栈已满!" << endl;
return false;
}
stack.base[stack.top] = value; // 以栈顶位置作为下标存储数据
stack.top++; // 栈顶自增一
return true;
}
// 栈顶元素出栈后,保存出栈元素返回
bool popStack(Stack& stack, ElemType& value) {
if (estimateStackEmpty(stack)) {
cout << "栈为空!" << endl;
return false;
}
stack.top--; // 栈顶自减一
value = stack.base[stack.top]; // 以栈顶位置作为下标的值赋值给value返回
return true;
}
bool gainStack(Stack& stack, ElemType& value) {
if (estimateStackEmpty(stack)) {
cout << "栈为空!" << endl;
return false;
}
value = stack.base[stack.top - 1]; // 将栈顶下标“假”减一后的值赋值给value返回
return true;
}
void deleteStack(Stack& stack) {
if (stack.base) {
delete stack.base;
stack.top = 0;
stack.base = NULL;
cout << "栈已释放内存!" << endl;
}
}
int main(void) {
Stack stack;
// 初始化栈
initStack(stack);
// 插入元素
int n = 0;
int value = 0;
cout << "请输入需要插入的元素个数:";
cin >> n;
while (n > 0) {
cin >> value;
insertStack(stack, value);
n--;
}
// 获取栈顶的元素
gainStack(stack, value);
cout << "当前栈顶的元素是:" << value << endl;
// 获取栈的元素个数
cout << "当前栈的元素个数是:" << stackSize(stack) << endl;
// 出栈
cout << "出栈顺序:" << endl;
while (!estimateStackEmpty(stack)) {
popStack(stack, value);
cout << value << " ";
}
cout << endl;
// 释放栈的内存
deleteStack(stack);
system("pause");
return 0;
}
课后作业:栈的链式存储结构
栈其实是可以使用链表来实现的,具体实现给大家自己去思考啦!
=
=
=
刚开始小编首先想到的是使用双向链表来实现,但是这样实属杀鸡使用宰牛刀!
于是乎再进一步思考了一会,发现单向循环链表也可以做!
使用单向循环链表实现图解:
但是还可以更加简便,那就是使用单向链表来实现!
希望下面两幅图片可以助你理解:使用单向链表实现
单向链表实现栈 代码:
#include <iostream>
#include <Windows.h>
using namespace std;
typedef int DateType;
// 循环单项链表
typedef struct _linkStack {
DateType date;
int size; // 统计栈的个数
struct _linkStack *next; // 头节点指向栈顶,其他节点指向下一个节点
}LinkList, LinkNode;
bool initLink(LinkList*& list); // 初始化双向链表的栈
bool insertNode(LinkList*& list, LinkNode*& node); // 尾插法入栈
bool popNode(LinkList*& list, DateType& value); // 出栈
bool gainNode(LinkList*& list, DateType& value); // 获取栈顶的值
bool clearLink(LinkList*& list); // 释放内存
void print_1(LinkList*& list); // 单纯输出
bool initLink(LinkList*& list) {
list = new LinkList; // 分配内存
if (!list) {
cout << "内存分配失败!" << endl;
return false;
}
list->date = -1;
list->size = 0;
list->next = NULL;
return true;
}
bool insertNode(LinkList*& list, LinkNode*& node) {
if (!list || !node) {
cout << "节点不存在!" << endl;
return false;
}
// 实现连接
node->next = list->next; // 新节点的next指向头节点的下一个节点
list->next = node; // 头节点的next指向新节点
list->size++; // 栈的元素个数加一
return true;
}
bool popNode(LinkList*& list, DateType& value) {
if (!list) {
cout << "节点不存在!" << endl;
return false;
}
value = list->next->date; // 将头节点的下一个节点的值赋值给value返回
list->size--; // 栈的元素个数减一
LinkNode* p = list->next; // 将头节点的下一个节点赋值给节点p
list->next = p->next; // 头节点的next指向p的next
delete p; // 释放p的内存
return true;
}
bool gainNode(LinkList*& list, DateType& value) {
if (!list) {
cout << "节点不存在!" << endl;
return false;
}
value = list->next->date; // 将头节点的下一个节点的值赋值给value返回
return true;
}
bool clearLink(LinkList*& list) {
if (!list) {
return false;
}
list->size = 0; // 栈的元素个数赋值0
LinkList* p = list; // 头节点赋值给p
while (p) { // p不为NULL执行循环
list = list->next; // 头节点只想自己的下一个节点
delete p; // 释放原头节点的内存
p = list; // 头节点赋值给p
}
cout << "释放成功!" << endl;
return true;
}
// 打印栈中的元素
void print_1(LinkList*& list) {
if (!list) {
cout << "节点不存在!" << endl;
return;
}
LinkList* p = list->next;
while (p) {
cout << p->date << ", ";
p = p->next;
}
cout << endl;
}
int main(void) {
LinkList* list = NULL;
LinkNode* node = NULL;
// 初始化
initLink(list);
// 插入元素
int n = 0;
int value = 0;
cout << "请输入入栈的个数:";
cin >> n;
while (n > 0) {
cin >> value;
node = new LinkNode;
node->date = value;
insertNode(list, node);
n--;
}
// 输出
cout << endl << endl << "插入元素后:" << endl;
print_1(list);
cout << "栈的元素个数是:" << list->size << endl;
// 出栈
popNode(list, value);
cout << endl << endl << "栈顶出栈后,出栈元素是:" << value << endl;
cout << "栈的元素个数是:" << list->size << endl;
cout << endl << endl << "栈的元素:" << endl;
print_1(list);
// 获取栈顶的元素
gainNode(list, value);
cout << endl << endl << "获取栈顶的元素是:" << value << endl;
// 释放内存
cout << endl << endl << "释放栈的内存:";
clearLink(list);
print_1(list);
system("pause");
return 0;
}
运行截图:
总结:
其实栈算是算法当中最简单的了,他不像堆、链表顺序表等那些那么繁杂!
重点理解他是如何入栈和出栈就可以了!