1-1 栈:数组实现
使用数组栈
栈是一种遵循后进先出(LIFO)原则的线性数据结构。可以通过将数组的末尾视为栈顶来使用数组实现栈。
使用数组实现栈时,我们需要维护:
- 一个整型数组用于存储元素。
- 一个变量 capacity 用于表示栈的最大容量。
- 一个变量 top 用于跟踪顶部元素的索引。初始时,top = -1 表示空栈。
class Stack
{
// array to store elements
int *arr;
// maximum size of stack
int capacity;
// index of top element
int top;
public:
// constructor
Stack(int cap)
{
capacity = cap;
arr = new int[capacity];
top = -1;
}
};
int main()
{
Stack mystack{ 5 };
return 0;
}

栈操作
基本的操作共有五种:一个是压栈(push), 弹栈(pop), 获得栈顶元素(peek), 栈的判空(isEmpty)和判满(isFull)
| 操作 | 函数 | 说明 | 例子 |
|---|---|---|---|
| 压栈 | push(x) |
将元素 x 放入栈顶。若栈满,输出 "Stack Overflow" | st.push(1); st.push(2); → 栈内: [1, 2] |
| 弹栈 | pop() |
移除并返回栈顶元素。若栈空,输出 "Stack Underflow",返回 -1 | st.pop(); → 返回 2,栈内: [1] |
| 取顶 | peek() ortop() |
返回栈顶元素但不移除。若栈空,输出 "Stack is Empty",返回 -1 | st.peek(); → 返回 1 |
| 判空 | isEmpty() |
检查栈是否为空,返回布尔值 | st.isEmpty(); → false |
| 判满 | isFull() |
检查栈是否已满,返回布尔值 | st.isFull(); → false |
压栈操作
向栈中添加一个元素。如果栈已满,则称为溢出状态。
- 在将元素压入栈之前,我们会检查栈是否已满。
- 如果栈已满(top == capacity - 1),则发生栈溢出,我们无法将该元素插入栈中。
- 否则,我们将 top 的值加 1(top = top + 1),并将新值插入到栈顶位置。
- 元素可以被压入栈中,直到达到栈的容量上限。
// push operation
void push(int x)
{
if (top == capacity - 1)
{
cout << "Stack Overflow\n";
return;
}
arr[++top] = x;
}
我们尝试将1,2依次压入栈中,完整代码如下:
# include <iostream>
class Stack
{
// array to store elements
int *arr;
// maximum size of stack
int capacity;
// index of top element
int top;
public:
// constructor
Stack(int cap)
{
capacity = cap;
arr = new int[capacity];
top = -1;
}
void push(int x)
{
if (top == capacity - 1)
{
std::cout << "Stack Overflow\n";
return;
}
arr[++top] = x;
}
};
int main()
{
Stack pushStack{2};
pushStack.push(1);
pushStack.push(2);
return 0;
}

我们通过Stack pushStack{2};使用列表初始化栈的对象pushStack最大容量初始化为2, 而依次将1、2依次压入栈中之后,这样栈就满了。 这时候我们再尝试压栈,使用pushStack.push(3);将第3个元素压入栈中,就会触发“Stack Overflow”并且返回空。
# include <iostream>
class Stack
{
// array to store elements
int *arr;
// maximum size of stack
int capacity;
// index of top element
int top;
public:
// constructor
Stack(int cap)
{
capacity = cap;
arr = new int[capacity];
top = -1;
}
void push(int x)
{
if (top == capacity - 1)
{
std::cout << "Stack Overflow\n";
return;
}
arr[++top] = x;
}
};
int main()
{
Stack pushStack{2};
pushStack.push(1);
pushStack.push(2);
pushStack.push(3);
return 0;
}
运行上面代码将输出
Stack Overflow
弹栈操作
从栈中移除一个元素。元素按与压入栈时的相反顺序弹出。如果栈为空,则称为栈下溢。
- 在从栈中弹出元素之前,我们会检查栈是否为空。
- 如果栈为空(top == -1),则发生栈下溢
Underflow,此时无法从栈中移除任何元素。 - 否则,我们会将 top 的值保存下来,将 top 的值减 1(top = top – 1),并返回保存的 top 值。
// pop operation
int pop()
{
if (top == -1)
{
cout << "Stack Underflow\n";
return -1;
}
return arr[top--];
}
为了使得例子更加方便,我们列表初始化Stack对象的最大容量为1, 然后对它进行弹栈操作, 就会触发Underflow并且返回-1。
#include <iostream>
class Stack
{
// array to store elements
int *arr;
// maximum size of stack
int capacity;
// index of top element
int top;
public:
// constructor
Stack(int cap)
{
capacity = cap;
arr = new int[capacity];
top = -1;
}
int pop()
{
if (top == -1)
{
std::cout << "Stack Underflow\n";
return -1;
}
return arr[top--];
}
};
int main()
{
Stack popStack{ 1 };
popStack.pop();
return 0;
}
运行该程序将输出
Stack Underflow
栈的取顶(Top)或查看(Peek)操作:
返回栈的顶部元素。
- 在从栈中返回顶部元素之前,我们会先检查栈是否为空。
- 如果栈为空(top == -1),则直接输出“栈为空”。
- 否则,返回存储在索引为 top 处的元素。
// peek (or top) operation
int peek()
{
if (top == -1)
{
cout << "Stack is Empty\n";
return -1;
}
return arr[top];
}
同样的,我们创建一个栈对象peekStack, 将它最大容量实例化为1, 然后检查它是否为空。
# include <iostream>
class Stack
{
// array to store elements
int *arr;
// maximum size of stack
int capacity;
// index of top element
int top;
public:
// constructor
Stack(int cap)
{
capacity = cap;
arr = new int[capacity];
top = -1;
}
// peek (or top) operation
int peek()
{
if (top == -1)
{
std::cout << "Stack is Empty\n";
return -1;
}
return arr[top];
}
};
int main()
{
Stack peekStack{ 1 };
peekStack.peek();
return 0;
}
运行上面例子,因为栈顶索引我们一开始构造初始化为top = -1, 当此时对栈对象peekStack取顶peekStack.peek();时,会触发Stack is Empty并且返回-1。
Stack is Empty

栈中的 isEmpty 操作:
如果栈为空,则返回 true;否则返回 false。
- 检查栈顶元素的值。
- 如果 (top == -1),则栈为空,因此返回 true。
- 否则,栈不为空,因此返回 false。
// check if stack is empty
bool isEmpty()
{
return top == -1;
}
同理,当此时对栈对象emptyStack执行判空emptyStack.isEmpty(),因为栈顶索引我们一开始构造初始化为top = -1, top 与 -1 比较,求值结果为相等,返回true, std::cout 默认对布尔值进行数值输出,true 会被隐式转换为 1。
# include <iostream>
class Stack
{
// array to store elements
int *arr;
// maximum size of stack
int capacity;
// index of top element
int top;
public:
// constructor
Stack(int cap)
{
capacity = cap;
arr = new int[capacity];
top = -1;
}
// check if stack is empty
bool isEmpty()
{
return top == -1;
}
};
int main()
{
Stack emptyStack{ 1 };
std::cout << emptyStack.isEmpty() << '\n';
return 0;
}
运行该例子将输出
1
相关内容
我们将在第4.9课——布尔值讨论布尔值以整数类型存储。
栈中的 isFull 操作:
如果栈已满,则返回 true;否则返回 false。
- 检查栈顶元素的值。
- 如果 (top == capacity-1),则栈已满,因此返回 true。
- 否则,栈未满,因此返回 false。
// check if stack is full
bool isFull()
{
return top == capacity - 1;
}
完整代码如下:
class Stack
{
// array to store elements
int *arr;
// maximum size of stack
int capacity;
// index of top element
int top;
public:
// constructor
Stack(int cap)
{
capacity = cap;
arr = new int[capacity];
top = -1;
}
// check if stack is full
bool isFull()
{
return top == capacity - 1;
}
};
int main()
{
Stack fullStack{ 2 };
std::cout << fullStack.isFull() << '\n';
return 0;
}
同理,当此时对栈对象emptyStack执行判满`fullStack.isFull()`,因为栈顶索引我们一开始构造初始化为`top = -1`, top 与 apacity - 1 比较,求值结果为不相等,返回false, std::cout 默认对布尔值进行数值输出,false 会被隐式转换为 0。这里还没有测试栈满的情况,我们会在下面完整的栈:数组实现中测试。
---
# 使用数组实现栈的完整实现
```cpp
#include <iostream>
class Stack
{
// array to store elements
int *arr;
// maximum size of stack
int capacity;
// index of top element
int top;
public:
// constructor
Stack(int cap)
{
capacity = cap;
arr = new int[capacity];
top = -1;
}
// push operation
void push(int x)
{
if (top == capacity - 1)
{
std::cout << "Stack Overflow\n";
return;
}
arr[++top] = x;
}
// pop operation
int pop() {
if (top == -1)
{
std::cout << "Stack Underflow\n";
return -1;
}
return arr[top--];
}
// peek (or top) operation
int peek()
{
if (top == -1)
{
std::cout << "Stack is Empty\n";
return -1;
}
return arr[top];
}
// check if stack is empty
bool isEmpty()
{
return top == -1;
}
// check if stack is full
bool isFull()
{
return top == capacity - 1;
}
};
int main()
{
Stack stack{4};
// pushing elements
stack.push(1);
stack.push(2);
// checking if stack is full
std::cout << "Is stack full: " << (stack.isFull() ? "Yes" : "No") << "\n";
// checking if stack is empty
std::cout << "Is stack empty: " << (stack.isEmpty() ? "Yes" : "No") << "\n";
// popping one element
std::cout << "Popped: " << stack.pop() << "\n";
// popping two element
std::cout << "Popped: " << stack.pop() << "\n";
// checking top element
std::cout << "Top element: " << stack.peek() << "\n";
// checking if stack is full
std::cout << "Is stack full: " << (stack.isFull() ? "Yes" : "No") << "\n";
// checking if stack is empty
std::cout << "Is stack empty: " << (stack.isEmpty() ? "Yes" : "No") << "\n";
return 0;
}
这里new[] 操作符的数组大小形参期望的是 std::size_t(通常是 unsigned long 或 unsigned long long),而我们传入的实参capacity是 int(有符号)。这里capacity 从有符号的转化为无符号的会触发符号转化警告, 如果我们当初将类型安全警告被提升为错误(-Werror)会报错,需要降低类型安全警告等级,或者定义capacity为std::size_t类型。

相关内容
我们将在第16.3节—— std::vector 与无符号长度及下标问题 中讨论设计者当初将下标选择为无符号。
程序将输出结果为

使用动态数组实现栈
当使用固定大小的数组时,栈的最大容量无法超过其初始大小。为克服这一限制,我们可以使用动态数组。动态数组会在元素被添加或移除时自动调整大小,这使得栈更加灵活。
在 C++ 中,我们可以使用 vector, 查看当前大小size函数将替代判满操作。
#include <iostream>
#include <vector>
class Stack
{
private:
std::vector<int> arr;
public:
// push operation
void push(int x)
{
arr.push_back(x);
}
// pop operation
int pop() {
if (arr.empty())
{
std::cout << "Stack Underflow\n";
return -1;
}
int val = arr.back();
arr.pop_back();
return val;
}
// peek (or top) operation
int peek()
{
if (arr.empty())
{
std::cout << "Stack is Empty\n";
return -1;
}
return arr.back();
}
// check if stack is empty
bool isEmpty()
{
return arr.empty();
}
// current size
int size()
{
return arr.size();
}
};
int main()
{
Stack stack;
// pushing elements
stack.push(x: 1);
stack.push(x: 2);
// checking current size
std::cout << "Current size: " << stack.size() << '\n';
// checking if stack is empty
std::cout << "Is stack empty: " << (stack.isEmpty() ? "Yes" : "No") << "\n";
// popping one element
std::cout << "Popped: " << stack.pop() << "\n";
// popping two element
std::cout << "Popped: " << stack.pop() << "\n";
// checking top element
std::cout << "Top element: " << stack.peek() << "\n";
// checking current size
std::cout << "Current size: " << stack.size() << '\n';
// checking if stack is empty
std::cout << "Is stack empty: " << (stack.isEmpty() ? "Yes" : "No") << "\n";
return 0;
}
运行程序将输出
Current size: 2
Is stack empty: No
Popped: 2
Popped: 1
Top element: Stack is Empty
-1
Current size: 0
Is stack empty: Yes


浙公网安备 33010602011771号