顺序存储的栈
首先我们简单了解下什么是栈,做个比喻,把书堆叠起来,然后再拿走就是栈的特点:
后进先出(Last In First Out),并且只能再栈顶进行操作(添加或删除)。
话不多说,上代码:
栈的结构
struct Stack {
int top;
string* data;
};
简单加几个功能看看
const int MAX = 10;
struct Stack {
int top;
string* data;
Stack() :top(0) { data = new string[MAX]; }
void push(string s);
void pop();
void show();
int count();
};
top 即指向栈顶,data为存储数据的数组;若要进行某些操作,只能通过控制top下标进行。
栈的基本操作
void Stack::push(string s) {
if (top == MAX - 1) return;
data[top++] = s;
}
void Stack::pop() {
if (top == 0) return;
top--;
}
看完发现,操作起来挺简单;实际上就是用数组体现出栈的特点,然后进行封装。C++语言中有一个头文件,就封装了栈Stack;
#include <stack>
stack<int> st;
st.push();
st.pop();
这样,基本的栈操作也能理解了。尽管stack库实际上的栈并非以顺序存储方式对栈进行封装,但这并不妨碍我们理解和使用栈stack;
两栈公用空间
两栈共享空间,顾名思义,一个空间,给两个栈用。
在代码上,如何体现呢? 实际上就是一个数组有俩“指针”,一个指向头,
一个指向尾;大概可以如下图理解:
结构
struct DoubleStack {
string* data;
int top1;
int top2;
}
基本操作
struct DoubleStack {
string* data;
int top1;
int top2;
DoubleStack() :top1(-1), top2(MAX_SIZE) { data = new string[MAX_SIZE]; }
void push(string s, int e) {
if (top1 + 1 == top2) return;
if (e == 1)
data[++top1] = s;
else
data[--top2] = s;
}
void pop(int e) { //e表选择栈1还是栈2进行该操作
if (top1 < -1 || top2 > MAX_SIZE) return;
if (e == 1)
top1--;
else
top2++;
}
void show(int e) {
if (e == 1) { //根据各个栈的边界条件进行相应遍历
for (int i = top1; i >= 0; --i)
cout << data[i] << endl;
}
else {
for (int i = top2; i < MAX_SIZE; ++i)
cout << data[i] << endl;
}
}
};
实际上就是多操作了一个指针,他们的“极限”你可以想象是趋向中间;
毕竟top1向右增长,top2向左;两个栈相向增长,反向减少;所以按照普通的栈建造即可;
需要多注意一下“满栈”的边界条件是:top1+1==top2
“空栈”的边界条件是:top1<0 和 top2>MAX_SIZE-1(数组最大长度)
以及在push 和 pop函数中需要多指明一下在哪个栈进行操作。
双栈公用空间这种结构适用于同类型数据,数据存储呈现反向关系的类型;
你看,栈1占有更多空间,栈2空间就少了;
链栈
同顺序存储对应的就是链式存储结构,链表和数组的存储模式相应。
下面看看在链表形式的栈的模样吧!
链栈的结构
struct Node {
string data;
Node* next;
};
struct LinkStack {
Node data;
Node* top;
}
像链表一样的构造,然后给这个链表栈的特征,就是链栈了。
(后进先出,仅一个操作端,直接将头结点作top栈顶即可)
链栈的基本操作
struct LinkStack {
Node data;
Node* top;
LinkStack() :data(), top(nullptr) {}
void LS_create();
void push(string s);
void pop();
void show();
};
栈初始化:
void LinkStack::LS_create() {
top = nullptr;
}
添加元素:
void LinkStack::push(string s) {
Node* p = new Node();
p->data = s;
p->next = top;
top = p;
}
删除元素:
void LinkStack::pop() {
if (top == nullptr) return;
Node* p = top->next;
delete top;
top = p;
}
实际上,这栈这一片大多都是使用链表的思想,前期链表打好基础,后面的栈又或是队列都是将链表“特殊化”,给予一个特征,反而比单纯的链表更容易编写一些;虽然在运用栈的时候大多不会再去整一个,花个时间自己重现一次程序,运行一下,还是能够快速掌握的。
参考: 程杰老师的《大话数据结构》