【问题标题】:implement stack with freelist feature in Python 2.7在 Python 2.7 中使用 freelist 功能实现堆栈
【发布时间】:2017-02-03 09:24:10
【问题描述】:

设计一个具有空闲列表功能的堆栈(即在一个节点被弹出后,它的空间可以被未来的推送操作重用,并保存在空闲列表中)。代码被简化了,在某些方面没有意义,我只是用下面的代码来展示堆栈与空闲列表的一般概念(作为原型)。

我的问题是关于如何设计pop 操作。我的困惑是因为在pop 操作之后返回了对节点的引用,例如在我的代码中有node1 = stack.pop(),调用pop 的客户端可能会使用对节点的引用,并且相同的节点引用是在空闲列表中回收并可能被其他 push 操作使用,如何解决此类冲突,并想知道有关使用堆栈、链表或队列设计空闲列表功能的一般建议。

MAXSIZE = 100
freeListHead = None

class StackNode:
    def __init__(self, value, nextNode):
        self.value = value
        self.nextNode = nextNode
class Stack:
    def __init__(self):
        self.top = None
        self.size = 0
    def peek(self):
        # return StackNode
        return self.top
    def push(self, value):
        global freeListHead
        if self.size >= MAXSIZE:
            raise Exception('stack full')
        node = freeListHead
        node.value = value
        freeListHead = node.nextNode
        node.nextNode = self.top
        self.top = node
        self.size += 1
    def pop(self):
        if self.size == 0:
            return None
        node = self.top
        self.top = self.top.nextNode
        self.size -= 1
        return node

if __name__ == "__main__":
    # initialization for nodes and link them to be a free list
    nodes = [StackNode(-1, None) for i in range(MAXSIZE)]
    freeListHead = nodes[0]
    for i in range(0, len(nodes)-1):
        nodes[i].nextNode = nodes[i+1]
    stack = Stack()
    stack.push(1)
    stack.push(50)
    stack.push(100)
    stack.push(200)
    print stack.peek().value
    node1 = stack.pop()
    print node1.value
    print stack.peek().value

【问题讨论】:

  • 当您的push 接受值而不是节点时,为什么要从pop 返回一个节点?对我来说,pop 返回与push 完全相同的东西会更有意义。
  • @niemmi,不错。这是一个不错的选择。如果我想要pushpop 处理StackNode 而不是int,是否有解决方案?
  • 它已经做到了,请参阅您的push 实现,它需要一个参数但不关心类型。以同样的方式,无论类型如何,您都可以让pop 返回node.value
  • 谢谢@niemmi,我的意思是如果我需要从pop 返回StackNode(出于封装的目的,因为StackNode 可以包装很多其他东西,而不仅仅是@987654341 @),有什么建议可以避免冲突吗?
  • 这样想:除了访问node.value 之类的属性之外,您还打算如何处理返回的node

标签: python python-2.7 stack


【解决方案1】:

您可以通过将value 返回给push 而不是保存该值的StackNode 来简化您的代码。无需担心内存消耗,因为您可以在返回结果之前将pop 中的node.value 设置为None。下面是一个简单的例子,说明这将如何在实践中发挥作用:

class Node:
    def __init__(self):
        self.nextNode = self.value = None

class Stack:
    def __init__(self, maxSize):
        self.maxSize = maxSize
        self.size = 0
        self.top = self.free = None

    def push(self, value):
        if self.size == self.maxSize:
            raise Exception('Stack full')

        if self.free:
            node = self.free
            self.free = node.next
        else:
            node = Node()

        node.value = value
        node.nextNode = self.top
        self.top = node
        self.size += 1

    def pop(self):
        if not self.top:
            raise Exception('Stack empty');
        node = self.top
        self.top = node.nextNode
        node.nextNode = self.free
        self.free = node
        self.size -= 1
        result = node.value
        node.value = None

        return result

stack = Stack(3)
for i in range(3):
    stack.push(list(range(i + 1)))

while stack.size:
    print(stack.pop())

输出:

[0, 1, 2]
[0, 1]
[0]

【讨论】:

  • 谢谢,投赞成票,但我担心如果你node.value = None,那么空闲列表没有任何意义,因为被弹出节点使用的内存空间不能被重用。
  • @LinMa 我不确定我是否关注你,因为弹出的节点在push 方法中被重用。假设堆栈将存储从pop 返回的值。那么下一次调用push 时,堆栈如何知道客户端是否将返回的value 存储在某处并且将在以后使用它,或者它是否可以以某种方式被堆栈重用?
  • 谢谢,并为回复投票,实际上我使用空闲列表的目的是节省创建新StackNode对象的时间/内存并重新使用value消耗的内存,您的代码可以实现目标(尤其是后者)?
  • @LinMa 这很重要,因为 Python 使用 garbage collection 并且只要对象被引用,GC 就无法收集该对象。因此,将pop 中的node.value 设置为None 允许GC 在调用者完成后立即释放对象。
  • @LinMa:是的,CPython 有垃圾收集功能。可以从docs.python.org/2/library/gc.html找到接口。
猜你喜欢
  • 2023-04-10
  • 2021-11-16
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-04-11
  • 2020-01-02
  • 2015-12-06
  • 2016-05-09
相关资源
最近更新 更多