【问题标题】:Implementing K stacks in one array algorithm在一个数组算法中实现 K 个堆栈
【发布时间】:2012-01-01 20:01:51
【问题描述】:

如何在一个数组中实现 K 个堆栈,以最佳的存储使用率(堆栈应该是动态的)?

【问题讨论】:

  • K 是提前知道的,还是动态变化的?
  • @zmbq : K 是一个常数,我们应该实现 K 个堆栈(例如 10),并且堆栈应该是动态的,这意味着,如果其中一个的大小增加到最大大小数组,其他的应该保持为零,并且数组的整个存储应该专用于那个大堆栈:)

标签: arrays linked-list stack


【解决方案1】:

好吧,如果您只担心空间使用,而不关心堆栈操作可能占用O(N),您可以使用数组的前几个单元格来管理堆栈:

Array[0] - 堆栈0结束

Array[1] - 堆栈 1 结束

...

Array[K-1] = 栈 K 结束

堆栈n 开始于Array[n-1] 并结束于Array[n] (exclusive - [Array[n-1], Array[n]) )If Array[n-1]==Array[n] 堆栈为空。第一个栈从 K 开始,所以首先是Array[0]..Array[K-1] = K

当你压入堆栈时,只需将其下方堆栈中的所有元素移动,并分别调整指针。

它将为您提供所需的内存限制。

【讨论】:

  • 这不是内存的最佳使用,因为如果一个堆栈大于 array_size / k 你会得到堆栈溢出,即使这是唯一使用的堆栈 --> 所以基本上你使用只有 array_size/k 个可用的 array_size 单元格。
  • 不,你不会,你只是在内存中移动其他堆栈。此方案将允许您使用每个数组单元(但第一个 K 除外),但堆栈操作将花费 O(N) 时间(最坏情况)。
【解决方案2】:

答案 1: 在开始时存储 K 个堆栈指针。现在将之后的第一个地址标记为地址 0(让生活更简单) 一个偶数 K 堆栈 (stack_0, stack_2, ...) 应该向上增长;奇数 K 堆栈 (stack_1, ..) 应该向下增长 在将数组初始化为 K/2 部分时(为了简单起见,假设 K 是偶数)。 stack0 从地址 0 开始 stack1 从 (arraySize / (k/2)) 开始向下增长 stack3 从 (arraySize / (k/2)) 开始向上增长

当将数据压入某个堆栈时,我们应该确保它不会溢出到相邻的堆栈,否则会抛出异常。

数组应如下所示: [[stack pointers][stack_0][stack_1]...[stack_k]] 其中 stack[0] 和 stack[1] 都共享相同的区域,因此它们可以最佳地利用可用的空间。

可以通过将每个大堆栈与一个小堆栈配对来进行进一步的优化(这可以通过检查堆栈随时间的行为来完成)。此外,将快速变化的数组与缓慢变化的数组组合在一起可能会有所帮助。

答案 2: 对此进行了更多思考,我看到我的第一个解决方案只保证使用 array_size/(k/2) (因为如果我们只有一个大小为 array_size/(k/2) 的数组,我们将得到堆栈溢出)。 以下(完全不切实际的)解决方案可以满足要求: 我们为我们的 k 个堆栈指针分配数组的开头,并从现在开始忽略该区域。 在数组的其余部分,我们将每个单元格视为一个结构 [data, previous, next]。

push(stack_i, data) -> 从堆栈指针区域获取 sp_i。然后转到该地址,填写“next”指针以指向数组中的下一个空单元格(我们可以将所有空白空间链接到另一个堆栈中,因此这是 o(1))。在“下一个”单元格中存储我们的数据,并填写“上一个”指针。更新 sp_i

pop(stack_i) -> 获取 sp_i。从该单元格中获取“数据”。该单元格中的“prev”是我们的新 sp_i。将旧的(现在为空的)单元格推送到空列表中。

【讨论】:

  • 这不会很有效。例如,如果您在 Stack 8 和 Stack 9 中有东西,它们之间有 10 个空闲元素,您将无法使用 stack5 中的所有其他元素(包括这 10 个)。
【解决方案3】:

噢噢噢,如果 K 也是动态的,你只需让 K 元素数组动态。让它变大仅仅意味着下推所有的堆栈。所以如果你不介意 O(N) 的 push 和 pop 操作,K 不应该是一个常数。

我想知道我是否得到了这份工作。

【讨论】:

  • 你得到这份工作了吗?
【解决方案4】:

我会使用一个包含所有空闲槽的队列,并在添加或弹出数据时更新该队列。这样,空间复杂度为O(N),其中N 是数组的大小。 popadd 操作是 O(1)

例如:你有一个大小为 S 和 K 堆栈的数组。您有一个队列,其中包含从 0 到 S-1 的所有空闲插槽。您添加的第一个值将位于第一个空闲槽(索引 0)中。然后从队列中弹出索引 0。添加或弹出数据到哪个堆栈都没有关系。如果弹出一个值,则将删除节点的槽的索引排入队列,并相应地设置指针或索引。

这是C++ 中的一个实现。我已经使用索引来指向下一个节点(毕竟我们使用的是数组),但是你可以使用指针或迭代器,没关系。

// TYPE: type of the stacks, SIZE: size of the array, STACKS: number of stacks
template <typename TYPE, size_t SIZE, size_t STACKS>
class KStacksInOneArray
{
// A node holds the data and a pointer or index to the next value
private:
    struct Node
    {
        TYPE data;
        int next = -1; // -1 is equivalent to nullptr:
                       // there is no next element
    };

public:
    KStacksInOneArray()
    {
        // initialize the free slots from 0 to SIZE - 1
        for (size_t idx = 0; idx < SIZE; ++idx)
            _freeSlots.push(idx);

        // initialize the heads, all to -1
        std::fill(_heads.begin(), _heads.end(), -1);
    }

    void pop(size_t stack)
    {
        // don't trust the user
        if (stack >= STACKS) throw std::out_of_range("there are only " + std::to_string(STACKS) + " stacks");
        if (isEmpty(stack)) throw std::out_of_range("cannot pop from an empty stack");

        // before destroying the node, get the new head  
        auto newHead = _arr[_heads[stack]].next;
        _arr[_heads[stack]] = Node{};

        // push the free slot on the queue and adjust the head
        _freeSlots.push(_heads[stack]);
        _heads[stack] = newHead;
    }

    const TYPE& top(size_t stack) const
    {
        if (stack >= STACKS) throw std::out_of_range("there are only 3 stacks");

        return _arr[_heads[stack]];
    }

    void add(size_t stack, TYPE data)
    {
        if (stack >= STACKS) throw std::out_of_range("there are only " + std::to_string(STACKS) + " stacks");
        if (_freeSlots.empty()) throw std::bad_alloc();

        // set the new node in the first free slot and 
        _arr[_freeSlots.front()] = {std::move(data), _heads[stack] != -1 ? _heads[stack] : -1};

        // update the head and remove the free slot from the queue
        _heads[stack] = _freeSlots.front();
        _freeSlots.pop();
    }

    bool isEmpty(size_t stack) const
    {
        if (stack >= STACKS) throw std::out_of_range("there are only " + std::to_string(STACKS) + " stacks");

        if (_heads[stack] == -1) return true;
        return false;
    }

private:
    std::vector<Node> _arr = std::vector<Node>(SIZE);
    std::array<int, STACKS> _heads;
    std::queue<int> _freeSlots;
};

【讨论】:

    猜你喜欢
    • 2015-08-07
    • 1970-01-01
    • 2010-12-15
    • 2021-09-06
    • 1970-01-01
    • 2017-12-21
    • 2023-03-04
    • 2010-11-26
    • 2015-08-17
    相关资源
    最近更新 更多