仅使用一种数据结构实现动态多栈(K 栈)
在本文中,我们将看到如何创建一个可以处理多个可增长大小的堆栈的数据结构。数据结构需要处理三个操作:
- push(x, stackNum) = pushes value x to the stack numbered stackNum
- pop(stackNum) = pop the top element from the stack numbered stackNum
- top(stackNum) = shows the topmost element of the stack stackNum.
例子:
Suppose the given multi stack is [{1, 2}, {4, 6}, {9, 10}]
Input: push(3, 0), top(0)
push(7, 1), top(1)
pop(2), top(2)
Output: 3, 7, 9
Explanation: When 3 is pushed in stack 0, the stack becomes {1, 2, 3}. So the top element is 3.
When 7 is pushed in stack 1, the stack becomes {4, 6, 7}. So the top element is 7.
When topmost element is popped from stack 2, the stack becomes {9}. So the topmost element is 9
方法:按照下面提到的方法来实现这个想法。
- 将每个堆栈的大小和顶部索引存储在数组sizes[]和topIndex[]中。
- 大小将存储在前缀和数组中(使用前缀数组和将帮助我们在 O(1) 时间内找到堆栈的开始/大小)
- 如果堆栈的大小达到最大保留容量,则扩大保留大小(乘以2)
- 如果堆栈的大小下降到保留大小的四分之一,则缩小保留大小(将其除以 2)
- 每次我们需要扩展/收缩数据结构中的堆栈时,其他堆栈的索引可能会发生变化,因此我们需要注意这一点。这是通过增加/减少 size[] 和 topIndex[] 数组的值来处理的(我们可以在 O(堆栈数)时间内做到这一点)。
下面是实现:
C++
#include
using namespace std;
template
// Class to implement multistack
class MultiStack {
int numberOfStacks;
vector values;
vector sizes, topIndex;
public:
// Constructor to create k stacks
// (by default 1)
MultiStack(int k = 1)
: numberOfStacks(k)
{
// reserve 2 elements for each stack first
values = vector(numberOfStacks << 1);
// For each stack store the index
// of the element on the top
// and the size (starting point)
sizes = vector(numberOfStacks);
topIndex = vector(numberOfStacks);
// Sizes is a prefix sum vector
for (int size = 2, i = 0; i < numberOfStacks;
i++, size += 2)
sizes[i] = size, topIndex[i] = size - 2;
}
// Push a value in a stack
void push(int stackNum, T val)
{
// Check if the stack is full,
// if so Expand it
if (isFull(stackNum))
Expand(stackNum);
// Add the value to the top of the stack
// and increment the top index
values[topIndex[stackNum]++] = val;
}
// Pop the top value and
// return it form a stack
T pop(int stackNum)
{
// If the stack is empty
// throw an error
if (empty(stackNum))
throw("Empty Stack!");
// Save the top value
T val = values[topIndex[stackNum] - 1];
// Set top value to default data type
values[--topIndex[stackNum]] = T();
// Shrink the reserved size if needed
Shrink(stackNum);
// Return the pop-ed value
return val;
}
// Return the top value form a stack
// Same as pop (but without removal)
T top(int stackNum)
{
if (empty(stackNum))
throw("Empty Stack!");
return values[topIndex[stackNum] - 1];
}
// Return the size of a stack
// (the number of elements in the stack,
// not the reserved size)
int size(int stackNum)
{
if (!stackNum)
return topIndex[0];
return topIndex[stackNum] - sizes[stackNum - 1];
}
// Check if a stack is empty or not
// (has no elements)
bool empty(int stackNum)
{
int offset;
if (!stackNum)
offset = 0;
else
offset = sizes[stackNum - 1];
int index = topIndex[stackNum];
return index == offset;
}
// Helper function to check
// if a stack size has reached
// the reserved size of that stack
bool isFull(int stackNum)
{
int offset = sizes[stackNum];
int index = topIndex[stackNum];
return index >= offset;
}
// Function to expand the reserved size
// of a stack (multiply by 2)
void Expand(int stackNum)
{
// Get the reserved_size of the stack()
int reserved_size = size(stackNum);
// Update the prefix sums (sizes)
// and the top index of the next stacks
for (int i = stackNum + 1; i < numberOfStacks; i++)
sizes[i] += reserved_size,
topIndex[i] += reserved_size;
// Update the size of the recent stack
sizes[stackNum] += reserved_size;
// Double the size of the stack by
// inserting 'reserved_size' elements
values.insert(values.begin() + topIndex[stackNum],
reserved_size, T());
}
// Function to shrink the reserved size
// of a stack (divide by2)
void Shrink(int stackNum)
{
// Get the reserved size and the current size
int reserved_size, current_size;
if (!stackNum)
reserved_size = sizes[0],
current_size = topIndex[0];
else
reserved_size
= sizes[stackNum] - sizes[stackNum - 1],
current_size
= topIndex[stackNum] - sizes[stackNum - 1];
// Shrink only if the size is
// lower than a quarter of the
// reserved size and avoid shrinking
// if the reserved size is 2
if (current_size * 4 > reserved_size
|| reserved_size == 2)
return;
// Divide the size by 2 and update
// the prefix sums (sizes) and
// the top index of the next stacks
int dif = reserved_size / 2;
for (int i = stackNum + 1; i < numberOfStacks; i++)
sizes[i] -= dif, topIndex[i] -= dif;
sizes[stackNum] -= dif;
// Erase half of the reserved size
values.erase(values.begin() + topIndex[stackNum],
values.begin() + topIndex[stackNum]
+ dif);
}
};
// Driver code
int main()
{
// create 3 stacks
MultiStack MStack(3);
// push elements in stack 0:
MStack.push(0, 21);
MStack.push(0, 13);
MStack.push(0, 14);
// Push one element in stack 1:
MStack.push(1, 15);
// Push two elements in stack 2:
MStack.push(2, 1);
MStack.push(2, 2);
MStack.push(2, 3);
// Print the top elements of the stacks
cout << MStack.top(0) << '\n';
cout << MStack.top(1) << '\n';
cout << MStack.top(2) << '\n';
return 0;
}
输出
14
15
3
时间复杂度:
- O(1) 用于top()函数。
- push()和pop()函数的摊销 O(1)。
辅助空间: O(N),其中 N 是堆栈数