什么是循环数据结构有用?

时间:2009-01-01 22:06:10

标签: python data-structures recursion cyclic-reference

我刚读完"Learning Python" by Mark Lutz and came across this code sample


>>> L = ['grail']
>>> L.append(L)
>>> L
['grail', [...]]

它被确定为循环数据结构。

所以我很想知道,这是我的问题:

在现实生活中使用什么“循环数据结构”?

似乎有点混乱,我认为这源于非常简短的代码示例......这里有几行使用相同的对象L


>>> L[0]
'grail'
>>> L[1][0]
'grail'
>>> L[1][1][0]
'grail'

12 个答案:

答案 0 :(得分:18)

很多事情。循环缓冲区,例如:你有一些带有正面和背面的数据集合,但是有任意数量的节点,而最后一个节点中的“下一个”项目应该会带你回到第一个。

图形结构通常是循环的; acyclicity是一个特例。例如,考虑一个包含旅行商问题中所有城市和道路的图表。


好的,这是一个特别的例子。我在科罗拉多州建立了一系列城镇:

V=["Boulder", "Denver", "Colorado Springs", "Pueblo", "Limon"]
然后,我建立了一对连接它们的道路的城市。

E=[["Boulder", "Denver"],
   ["Denver", "Colorado Springs"],
   ["Colorado Springs", "Pueblo"],
   ["Denver", "Limon"],
   ["Colorado Springs", "Limon"]]

这有一堆循环。例如,您可以从科罗拉多斯普林斯,利蒙到丹佛,然后返回科罗拉多斯普林斯。

如果您创建的数据结构包含V中的所有城市和E中的所有道路,那么这就是图形数据结构。这个图表有周期。

答案 1 :(得分:6)

我最近创建了一个循环数据结构来表示八个基本和有序方向。它对每个方向都有用,可以了解它的邻居。例如,Direction.North知道Direction.NorthEast和Direction.NorthWest是它的邻居。

这是循环的,因为每个neighor都知道它的邻居,直到它全速摆动(“ - >”代表顺时针方向):

北 - > NorthEast - >东 - > SouthEast - >南 - > SouthWest - >西 - > NorthWest - >北 - > ...

请注意我们回到了北方。

这允许我做这样的事情(在C#中):

public class Direction
{
    ...
    public IEnumerable<Direction> WithTwoNeighbors
    {
        get {
           yield return this;
           yield return this.CounterClockwise;
           yield return this.Clockwise;
        }
    }
}
...
public void TryToMove (Direction dir)
{
    dir = dir.WithTwoNeighbors.Where (d => CanMove (d)).First ()
    Move (dir);
}

事实证明这非常方便,并且使很多事情变得复杂得多。

答案 2 :(得分:1)

嵌套结构可用于垃圾收集器的测试用例。

答案 3 :(得分:1)

它有点令人困惑,因为它是一个包含自身的列表,但我理解它的方式是不将L视为列表,而是一个节点,而不是列表中的东西,你想到的它可以作为此节点可以访问的其他节点。

举一个更真实的例子,把它们想象成一个城市的飞行路径。

所以chicago = [丹佛,洛杉矶,纽约市,芝加哥](实际上你不会列出芝加哥本身,但为了举例,你可以从芝加哥到达芝加哥)

然后你有丹佛= [凤凰,菲利普]等等。

phoenix = [芝加哥,纽约]

现在你有来自

的循环数据
  

芝加哥 - &gt;芝加哥

但也

  

芝加哥 - &gt;丹佛 - &gt;凤凰 - &gt;芝加哥

现在你有:

chicago[0] == denver
chicago[0][0] == phoenix
chicago[0][0][0] == chicago

答案 4 :(得分:1)

L只包含对自身的引用作为其中一个元素。没什么特别的。

循环结构有一些明显的用途,其中最后一个元素知道第一个元素。但是常规python列表已经涵盖了这个功能。

您可以使用L获取[-1]的最后一个元素。您可以将python列表用作append()pop()的队列。你可以拆分python列表。这是循环数据结构的常规用法。

>>> L = ['foo', 'bar']
>>> L.append(L)
>>> L
['foo', 'bar', [...]]
>>> L[0]
'foo'
>>> L[1]
'bar'
>>> L[2]
['foo', 'bar', [...]]
>>> L[2].append('baz')
>>> L
['foo', 'bar', [...], 'baz']
>>> L[2]
['foo', 'bar', [...], 'baz']
>>> L[2].pop()
'baz'
>>> L
['foo', 'bar', [...]]
>>> L[2]
['foo', 'bar', [...]]

答案 5 :(得分:1)

deterministic finite automata迭代的数据结构通常是周期性的。

答案 6 :(得分:0)

一个例子是一个链表,其中最后一个项指向第一个。这将允许您创建固定数量的项目,但始终能够获得下一个项目。

答案 7 :(得分:0)

在进行晶格模拟时,经常使用循环/环形边界条件。通常一个简单的lattice[i%L]就足够了,但我想人们可以创建一个循环的格子。

答案 8 :(得分:0)

假设您的存储空间有限,并且数据会不断累积。在许多现实生活中,您不介意删除旧数据,但您不想移动数据。你可以使用循环向量;使用大小为N的向量v和两个特殊索引实现:begin和end,它们从0开始。

现在插入“新”数据是这样的:

v[end] = a;
end = (end+1) % N;
if (begin == end)
  begin = (begin+1) % N;

您可以插入“旧”数据并以类似方式删除“旧”或“新”数据。 扫描矢量就像这样

for (i=begin; i != end; i = (i+1) % N) {
 // do stuff
}

答案 9 :(得分:0)

循环数据结构通常用于表示循环关系。这听起来很明显,但它的发生比你想象的要多。我无法想到任何时候我都使用了非常复杂的循环数据结构,但双向关系相当普遍。例如,假设我想创建一个IM客户端。我可以这样做:

class Client(object):
    def set_remote(self, remote_client):
        self.remote_client = remote_client

    def send(self, msg):
        self.remote_client.receive(msg)

    def receive(self, msg):
        print msg

Jill = Client()
Bob = Client()
Bob.set_remote(Jill)    
Jill.set_remote(Bob)

然后如果Bob想要向Jill发送消息,你可以这样做:

Bob.send("Hi, Jill!")

当然,吉尔可能想要回复一条消息,所以她可以这样做:

Jill.send("Hi, Bob!")

不可否认,这是一个人为的例子,但它应该为您提供一个何时可能需要使用循环数据结构的示例。

答案 10 :(得分:0)

任何类型的对象层次结构,父母知道他们的孩子和孩子都知道他们的父母。我总是不得不在ORM中处理这个问题,因为我希望数据库知道他们的表和表,以了解他们所属的数据库,等等。

答案 11 :(得分:0)

让我们看一个实际的例子。

让我们说我们正在为游戏编写菜单导航。我们想为每个菜单项存储

  1. 条目名称
  2. 按下后我们会看到另一个菜单。
  3. 按菜单时执行的操作。
  4. 按下菜单项后,我们将激活菜单项操作,然后移至下一个菜单。所以我们的菜单会是一个简单的词典列表,如下所示:

    options,start_menu,about = [],[],[]
    
    def do_nothing(): pass
    
    about += [
        {'name':"copyright by...",'action':None,'menu':about},
        {'name':"back",'action':do_nothing,'menu':start_menu}
        ]
    options += [
        {'name':"volume up",'action':volumeUp,'menu':options},
        {'name':"save",'action':save,'menu':start_menu},
        {'name':"back without save",'action':do_nothing,'menu':start_menu}
        ]
    start_menu += [
        {'name':"Exit",'action':f,'menu':None}, # no next menu since we quite
        {'name':"Options",'action':do_nothing,'menu':options},
        {'name':"About",'action':do_nothing,'menu':about}
        ]
    

    了解about是如何循环的:

    >>> print about
    [{'action': None, 'menu': [...], 'name': 'copyright by...'},#etc.
    # see the ellipsis (...)
    

    当按下菜单项时,我们将发出以下点击功能:

    def menu_item_pressed(item):
        log("menu item '%s' pressed" % item['name'])
        item['action']()
        set_next_menu(item['menu'])
    

    现在,如果我们没有循环数据结构,我们将无法拥有指向自身的菜单项,例如,在按下音量增加功能后,我们将不得不保留选项菜单。

    如果无法实现循环数据结构,我们必须自己实现,例如菜单项将是:

    class SelfReferenceMarkerClass: pass
    #singleton global marker for self reference
    SelfReferenceMarker = SelfReferenceMarkerClass()
    about += [
        {'name':"copyright by...",'action':None,'menu':srm},
        {'name':"back",'action':do_nothing,'menu':start_menu}
        ]
    

    menu_item_pressed函数将是:

    def menu_item_pressed(item):
        item['action']()
        if (item['menu'] == SelfReferenceMarker):
            set_next_menu(get_previous_menu())
        else:
            set_next_menu(item['menu'])
    

    第一个例子稍微好一点,但是,不支持自我引用并不是什么大不了的恕我直言,因为它很容易克服这个限制。

    菜单示例类似于具有自引用的图形,其中我们通过顶点指针列表存储图形(每个顶点是指向其他顶点的指针列表)。在这个例子中,我们需要自我边(一个指向自身的顶点),因此python对循环数据结构的支持很有用。