03.01、[简单] 三合一
1、题目描述
三合一。描述如何只用一个数组来实现三个栈。
你应该实现push(stackNum, value)
、pop(stackNum)
、isEmpty(stackNum)
、peek(stackNum)
方法。stackNum
表示栈下标,value
表示压入的值。
构造函数会传入一个stackSize
参数,代表每个栈的大小。
2、方法思路
-
数组表示栈:我们可以使用一个数组
arr
来表示三个栈。数组被划分为三个部分,每部分存储一个栈的元素。栈 1 的索引范围是[0, stackSize-1]
,栈 2 的范围是[stackSize, 2*stackSize-1]
,栈 3 的范围是[2*stackSize, 3*stackSize-1]
。 -
辅助指针数组:用一个长度为 3 的数组
tops
,存储每个栈的栈顶索引(即当前元素的存储位置),通过栈的编号stackNum
来访问对应的栈。 -
操作限制:
- 在进行
push
操作时,需要检查栈是否已经满了。 -
pop
和peek
操作在栈为空时应返回 -1。
- 在进行
3、代码实现
class TripleInOne {
private:
vector<int> arr; // 用来存储三个栈的数据
vector<int> tops; // 栈指针, 指向三个栈的下一个可用位置
int stackSize; // 每个栈的最大容量
public:
// 初始化: 设置栈的大小, 初始化数组和栈顶指针
TripleInOne(int stackSize) {
this->stackSize = stackSize; // 保存栈的大小
arr.resize(3 * stackSize); // 数组容量是三个栈的总容量
// 初始化三个栈的指针
// 栈 1 从 0 开始, 栈 2 从 stackSize 开始, 栈 3 从 2*stackSize 开始
tops = {0, stackSize, 2 * stackSize};
}
// 向指定的栈中压入一个元素
void push(int stackNum, int value) {
// 检查当前栈是否已满
if (tops[stackNum] < (stackNum + 1) * stackSize) {
arr[tops[stackNum]++] = value; // 插入值并更新栈顶索引
}
}
// 从指定的栈中弹出栈顶元素
int pop(int stackNum) {
if (isEmpty(stackNum)) { // 栈为空时返回-1
return -1;
}
return arr[--tops[stackNum]]; // 弹出栈顶元素并更新栈顶索引
}
// 查看指定栈的栈顶元素
int peek(int stackNum) {
// 检查栈是否为空
if (isEmpty(stackNum)) { // 栈为空时返回-1
return -1;
}
return arr[tops[stackNum] - 1]; // 返回栈顶元素
}
// 判断指定栈是否为空
bool isEmpty(int stackNum) {
// 如果栈指针等于栈的起始位置,说明栈为空
return tops[stackNum] == stackSize * stackNum;
}
};
4、代码解析
-
构造函数
TripleInOne(int stackSize)
:- 初始化
arr
数组大小为3 * stackSize
,以容纳三个栈的数据。 - 初始化
tops
数组,分别为三个栈的初始位置:栈 1 为 0,栈 2 为stackSize
,栈 3 为2 * stackSize
。
- 初始化
-
push(int stackNum, int value)
:- 先检查当前栈是否已满(通过检查指针是否超出该栈的边界),若未满,则将值压入并更新指针。
-
pop(int stackNum)
:- 检查栈是否为空,若为空则返回 -1;否则更新指针并返回栈顶元素。
-
peek(int stackNum)
:- 检查栈是否为空,若为空则返回 -1;否则返回栈顶元素。
-
isEmpty(int stackNum)
:- 通过比较指针位置是否等于栈的初始位置来判断栈是否为空。
5、时间复杂度
-
push
、pop
、peek
和isEmpty
操作的时间复杂度均为 O(1),因为每次操作只需更新栈指针或访问数组中的一个元素。
这个解决方案使用了固定大小的数组来实现三个栈的分隔,逻辑简单且效率高。在面试中这是一个常见的问题,考察你对栈和数组的理解,以及如何在限制条件下实现数据结构。