基本概念

栈的定义

​ 栈(Stack)是只允许在一端进行插入或删除的线性表。首先栈是一种线性表,但限定这种线性表只能在某一端进行插入和删除操作

1

1

栈顶(Top):线性表允许进行插入删除的那一端。
栈底(Bottom):固定的,不允许进行插入和删除的另一端。
空栈:不含任何元素的空表。

栈又称为后进先出(Last In First Out)的线性表,简称LIFO结构

栈的常见基本操作

  • InitStack(&S):初始化一个空栈S
  • StackEmpty(S):判断一个栈是否为空,若栈为空则返回true,否则返回false
  • Push(&S, x):进栈(栈的插入操作),若栈S未满,则将x加入使之成为新栈顶
  • Pop(&S, &x):出栈(栈的删除操作),若栈S非空,则弹出栈顶元素,并用x返回
  • GetTop(S, &x):读栈顶元素,若栈S非空,则用x返回栈顶元素
  • DestroyStack(&S):栈销毁,并释放S占用的存储空间(“&”表示引用调用)

栈的顺序存储结构

栈的顺序存储

​ 采用顺序存储的栈称为顺序栈,它利用一组地址连续的存储单元存放自栈底到栈顶的数据元素,同时附设一个指针(top)指示当前栈顶元素的位置。
若存储栈的长度为StackSize,则栈顶位置top必须小于StackSize。当栈存在一个元素时,top等于0,因此通常把空栈的判断条件定位top等于-1。
​ 若现在有一个栈,StackSize是5,则栈的普通情况、空栈、满栈的情况分别如下图所示:

2.1

2.1

代码实现

代码实现与顺序表的思路差不多。不过栈是后进先出。头文件如下所示。

c++
1
2
3
4
5
6
7
8
9
10
#include<String>
#include<iostream>
using namespace std;
typedef int elemtype;

typedef struct {
elemtype* elem;
int top;
int size;
}stack;

实现的功能如下所示。进栈函数可改成动态的。若栈已满可扩容,思路与顺序表扩容一样。以下代码并未实现。

c++
1
2
3
4
5
6
7
bool Initstack(stack& S , int size);//初始化栈
bool stackempty(stack* S);//判断栈是否为空
bool push(stack* S, elemtype x);//进栈,只能在顶层
bool pop(stack* S, elemtype x);//出栈,出顶层的
elemtype gettop(stack* S);//读取栈顶元素
bool delstack(stack &S);//销毁栈,释放空间
void display(stack* S);//显示栈中元素

函数具体细节如下所示

c++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
bool Initstack(stack& S, int size) {//初始化栈
S.elem = new elemtype[size];
S.size = size;
if (S.elem == NULL){
return false;
}
S.top = -1;
return true;
}
bool stackempty(stack* S) {//判断栈是否为空
if (S->top == -1){
return true;
}
return false;
}

bool push(stack* S, elemtype x) {//进栈,只能在顶层
if (S->top == (S->size -1)){
return false;
}
S->top++;
S->elem[S->top] = x;
return true;
}
bool pop(stack* S, elemtype x) {//出栈,出顶层的
if (S->top == -1){
return false;
}
x = S->elem[S->top];
S->top--;
return true;
}
elemtype gettop(stack* S) {//读取栈顶元素
elemtype x;
if (S->top == -1){
return -1;
}
x = S->elem[S->top];
return x;
}
bool delstack(stack& S) {//销毁栈,释放空间
S.elem = NULL;
S.top = -1;
S.size = 0;
delete[] S.elem;
if (S.elem == NULL){
return true;
}
return false;
}

void display(stack* S) {//显示栈中元素
int n = S->top;
while (n>-1){
cout << S->elem[n];
n--;
}
cout << endl;
}

调试主代码如下所示。

c++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
int main () {
stack S;
int x = 0;
Initstack(S,4);
stackempty(&S);
x = gettop(&S);
cout << "top is : " << x << endl;
cout << " input 4 num :" <<endl;
for (int i = 0; i < 4; i++){
cin >> x;
push(&S, x);
}
display(&S);
pop(&S,x);
display(&S);
bool re = delstack(S);
if (re == 1) {
cout << "del success" << endl;
}
return 0;
}

实验结果

3

3

共享栈(两栈共享空间)

概念

​ 利用栈底位置相对不变的特征,可让两个顺序栈共享一个一维数组空间,将两个栈的栈底分别设置在共享空间的两端,两个栈顶向共享空间的中间延伸,如下图所示:

4

4

​ 两个栈的栈顶指针都指向栈顶元素,top0=-1时0号栈为空,top1=MaxSize时1号栈为空;仅当两个栈顶指针相邻(top0+1=top1)时,判断为栈满。当0号栈进栈时top0先加1再赋值,1号栈进栈时top1先减一再赋值出栈时则刚好相反。

代码实现与顺序栈差不多。就是两个栈共享一段存储空间,元素入栈、出栈时需要判断对哪个栈进行操作。

栈的链式存储结构

链栈

​ 采用链式存储的栈称为链栈,链栈的优点是便于多个栈共享存储空间和提高其效率,且不存在栈满上溢的情况。通常采用单链表实现,并规定所有操作都是在单链表的表头进行。这里规定链栈没有头节点Lhead指向栈顶元素,如下图所示。

5

5

对于空栈来说,链表原定义是头指针指向空,那么链栈的空其实就是top=NULL的时候。

链栈进栈

​ 对于链栈的进栈push操作,假设元素值为e的新节点是s,top为栈顶指针,示意图如下:

6.1

6.1

链栈的出栈

​ 链栈的出栈pop操作,也是很简单的三句操作。假设变量p用来存储要删除的栈顶结点,将栈顶指针下移以为,最后释放p即可,如下图所示:

7

7

代码实现

​ 链栈的进栈相当于链表的前向插入,first in last out。以下代码思想主要是借鉴list链表的构建方法,使用head指针一直指向top。入栈时则head指向新的node,新的node的next指向之前的top。

头文件如下所示

c++
1
2
3
4
5
6
7
8
9
#include<String>
#include<iostream>

using namespace std;
typedef struct Node {
int data;//数据域
Node* next;//结构体指针
int cout;
}Node;

实现的函数如下

c++
1
2
3
4
5
void Initstack(Node *head);//初始化栈
Node* addnode(Node *head, int data); //入栈
int display(Node* head, int x);//遍历打印链表,查找元素个数
Node* findnode(Node* head, int x);//查找指针位置
bool delnode(Node* head, int n);//出栈

具体函数细节如下

c++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
void Initstack(Node* head) {//初始化栈
head->next = NULL;
head->cout = 0;
}

Node* addnode(Node* head, int data) {//入栈
Node* s = new Node; // 分配结点新空间
Node* tail = NULL;
s->data = data;
if (head->cout ==0){
head->next = s;
s->next = NULL;
head->cout++;
}
else{
s->next = head->next;
head->next = s;
head->cout++;
}
return head;
}

int display(Node* head, int x) {//遍历打印链表,查找元素个数
int count = 0;
Node* p = head->next;
cout << p->data << " ";
cout << "链栈中的元素有:";
while (p!= NULL) { //p本身就是指针变量
cout << p->data << " ";
if (x == p->data) {
count++;
}
p = p->next;
}
return count;
}

Node* findnode(Node* head, int x) {//查找指针位置
Node* p = head->next;
while (p != NULL) {
if (x == p->data) {
return p;
}
p = p->next;
}
return NULL;
}

bool delnode(Node* head, int n) {//出栈
Node* p = head->next;
if (p ==NULL){//栈中没有元素
return false;
}
n = p->data;
Node* after = p->next;
cout << "ssssss: " << after->data;
head->next = after;
head->cout--;
delete[] p;
return true;
}

实验截图如下所示

8

8

数据结构:栈和队列(Stack & Queue)【详解】-CSDN博客