Python中的高階資料結構

jobbole發表於2014-04-20

 資料結構

  資料結構的概念很好理解,就是用來將資料組織在一起的結構。換句話說,資料結構是用來儲存一系列關聯資料的東西。在Python中有四種內建的資料結構,分別是List、Tuple、Dictionary以及Set。大部分的應用程式不需要其他型別的資料結構,但若是真需要也有很多高階資料結構可供選擇,例如Collection、Array、Heapq、Bisect、Weakref、Copy以及Pprint。本文將介紹這些資料結構的用法,看看它們是如何幫助我們的應用程式的。

  關於四種內建資料結構的使用方法很簡單,並且網上有很多參考資料,因此本文將不會討論它們。

  1. Collections

  collections模組包含了內建型別之外的一些有用的工具,例如Counter、defaultdict、OrderedDict、deque以及nametuple。其中Counter、deque以及defaultdict是最常用的類。

  1.1 Counter()

  如果你想統計一個單詞在給定的序列中一共出現了多少次,諸如此類的操作就可以用到Counter。來看看如何統計一個list中出現的item次數:

from collections import Counter

li = ["Dog", "Cat", "Mouse", 42, "Dog", 42, "Cat", "Dog"]
a = Counter(li)
print a # Counter({'Dog': 3, 42: 2, 'Cat': 2, 'Mouse': 1})

  若要統計一個list中不同單詞的數目,可以這麼用:

from collections import Counter

li = ["Dog", "Cat", "Mouse", 42, "Dog", 42, "Cat", "Dog"]
a = Counter(li)
print a # Counter({'Dog': 3, 42: 2, 'Cat': 2, 'Mouse': 1})

print len(set(li)) # 4

  如果需要對結果進行分組,可以這麼做:

from collections import Counter

li = ["Dog", "Cat", "Mouse","Dog","Cat", "Dog"]
a = Counter(li)

print a # Counter({'Dog': 3, 'Cat': 2, 'Mouse': 1})

print "{0} : {1}".format(a.values(),a.keys())  # [1, 3, 2] : ['Mouse', 'Dog', 'Cat']

print(a.most_common(3)) # [('Dog', 3), ('Cat', 2), ('Mouse', 1)]

  以下的程式碼片段找出一個字串中出現頻率最高的單詞,並列印其出現次數。

import re
from collections import Counter

string = """   Lorem ipsum dolor sit amet, consectetur
    adipiscing elit. Nunc ut elit id mi ultricies
    adipiscing. Nulla facilisi. Praesent pulvinar,
    sapien vel feugiat vestibulum, nulla dui pretium orci,
    non ultricies elit lacus quis ante. Lorem ipsum dolor
    sit amet, consectetur adipiscing elit. Aliquam
    pretium ullamcorper urna quis iaculis. Etiam ac massa
    sed turpis tempor luctus. Curabitur sed nibh eu elit
    mollis congue. Praesent ipsum diam, consectetur vitae
    ornare a, aliquam a nunc. In id magna pellentesque
    tellus posuere adipiscing. Sed non mi metus, at lacinia
    augue. Sed magna nisi, ornare in mollis in, mollis
    sed nunc. Etiam at justo in leo congue mollis.
    Nullam in neque eget metus hendrerit scelerisque
    eu non enim. Ut malesuada lacus eu nulla bibendum
    id euismod urna sodales.  """

words = re.findall(r'\w+', string) #This finds words in the document

lower_words = [word.lower() for word in words] #lower all the words

word_counts = Counter(lower_words) #counts the number each time a word appears
print word_counts

# Counter({'elit': 5, 'sed': 5, 'in': 5, 'adipiscing': 4, 'mollis': 4, 'eu': 3, 
# 'id': 3, 'nunc': 3, 'consectetur': 3, 'non': 3, 'ipsum': 3, 'nulla': 3, 'pretium':
# 2, 'lacus': 2, 'ornare': 2, 'at': 2, 'praesent': 2, 'quis': 2, 'sit': 2, 'congue': 2, 'amet': 2, 
# 'etiam': 2, 'urna': 2, 'a': 2, 'magna': 2, 'lorem': 2, 'aliquam': 2, 'ut': 2, 'ultricies': 2, 'mi': 2, 
# 'dolor': 2, 'metus': 2, 'ac': 1, 'bibendum': 1, 'posuere': 1, 'enim': 1, 'ante': 1, 'sodales': 1, 'tellus': 1,
# 'vitae': 1, 'dui': 1, 'diam': 1, 'pellentesque': 1, 'massa': 1, 'vel': 1, 'nullam': 1, 'feugiat': 1, 'luctus': 1, 
# 'pulvinar': 1, 'iaculis': 1, 'hendrerit': 1, 'orci': 1, 'turpis': 1, 'nibh': 1, 'scelerisque': 1, 'ullamcorper': 1,
# 'eget': 1, 'neque': 1, 'euismod': 1, 'curabitur': 1, 'leo': 1, 'sapien': 1, 'facilisi': 1, 'vestibulum': 1, 'nisi': 1, 
# 'justo': 1, 'augue': 1, 'tempor': 1, 'lacinia': 1, 'malesuada': 1})

  1.2 Deque

  Deque是一種由佇列結構擴充套件而來的雙端佇列(double-ended queue),佇列元素能夠在佇列兩端新增或刪除。因此它還被稱為頭尾連線列表(head-tail linked list),儘管叫這個名字的還有另一個特殊的資料結構實現。

  Deque支援執行緒安全的,經過優化的append和pop操作,在佇列兩端的相關操作都能夠達到近乎O(1)的時間複雜度。雖然list也支援類似的操作,但是它是對定長列表的操作表現很不錯,而當遇到pop(0)和insert(0, v)這樣既改變了列表的長度又改變其元素位置的操作時,其複雜度就變為O(n)了。

  來看看相關的比較結果:

import time
from collections import deque

num = 100000

def append(c):
    for i in range(num):
        c.append(i)

def appendleft(c):
    if isinstance(c, deque):
        for i in range(num):
            c.appendleft(i)
    else:
        for i in range(num):
            c.insert(0, i)
def pop(c):
    for i in range(num):
        c.pop()

def popleft(c):
    if isinstance(c, deque):
        for i in range(num):
            c.popleft()
    else:
        for i in range(num):
            c.pop(0)

for container in [deque, list]:
    for operation in [append, appendleft, pop, popleft]:
        c = container(range(num))
        start = time.time()
        operation(c)
        elapsed = time.time() - start
        print "Completed {0}/{1} in {2} seconds: {3} ops/sec".format(
              container.__name__, operation.__name__, elapsed, num / elapsed)

# Completed deque/append in 0.0250000953674 seconds: 3999984.74127 ops/sec
# Completed deque/appendleft in 0.0199999809265 seconds: 5000004.76838 ops/sec
# Completed deque/pop in 0.0209999084473 seconds: 4761925.52225 ops/sec
# Completed deque/popleft in 0.0199999809265 seconds: 5000004.76838 ops/sec
# Completed list/append in 0.0220000743866 seconds: 4545439.17637 ops/sec
# Completed list/appendleft in 21.3209998608 seconds: 4690.21155917 ops/sec
# Completed list/pop in 0.0240001678467 seconds: 4166637.52682 ops/sec
# Completed list/popleft in 4.01799988747 seconds: 24888.0046791 ops/sec

  另一個例子是執行基本的佇列操作:

from collections import deque
q = deque(range(5))
q.append(5)
q.appendleft(6)
print q
print q.pop()
print q.popleft()
print q.rotate(3)
print q
print q.rotate(-1)
print q

# deque([6, 0, 1, 2, 3, 4, 5])
# 5
# 6
# None
# deque([2, 3, 4, 0, 1])
# None
# deque([3, 4, 0, 1, 2])

  譯者注:rotate是佇列的旋轉操作,Right rotate(正引數)是將右端的元素移動到左端,而Left rotate(負引數)則相反。

  1.3 Defaultdict

  這個型別除了在處理不存在的鍵的操作之外與普通的字典完全相同。當查詢一個不存在的鍵操作發生時,它的default_factory會被呼叫,提供一個預設的值,並且將這對鍵值儲存下來。其他的引數同普通的字典方法dict()一致,一個defaultdict的例項同內建dict一樣擁有同樣地操作。

  defaultdict物件在當你希望使用它存放追蹤資料的時候很有用。舉個例子,假定你希望追蹤一個單詞在字串中的位置,那麼你可以這麼做:

from collections import defaultdict

s = "the quick brown fox jumps over the lazy dog"

words = s.split()
location = defaultdict(list)
for m, n in enumerate(words):
    location[n].append(m)

print location

# defaultdict(<type 'list'>, {'brown': [2], 'lazy': [7], 'over': [5], 'fox': [3],
# 'dog': [8], 'quick': [1], 'the': [0, 6], 'jumps': [4]})

  是選擇lists或sets與defaultdict搭配取決於你的目的,使用list能夠儲存你插入元素的順序,而使用set則不關心元素插入順序,它會幫助消除重複元素。

from collections import defaultdict

s = "the quick brown fox jumps over the lazy dog"

words = s.split()
location = defaultdict(set)
for m, n in enumerate(words):
    location[n].add(m)

print location

# defaultdict(<type 'set'>, {'brown': set([2]), 'lazy': set([7]), 
# 'over': set([5]), 'fox': set([3]), 'dog': set([8]), 'quick': set([1]), 
# 'the': set([0, 6]), 'jumps': set([4])})

  另一種建立multidict的方法:

s = "the quick brown fox jumps over the lazy dog"
d = {}
words = s.split()

for key, value in enumerate(words):
    d.setdefault(key, []).append(value)
print d

# {0: ['the'], 1: ['quick'], 2: ['brown'], 3: ['fox'], 4: ['jumps'], 5: ['over'], 6: ['the'], 7: ['lazy'], 8: ['dog']}

  一個更復雜的例子:

class Example(dict):
    def __getitem__(self, item):
        try:
            return dict.__getitem__(self, item)
        except KeyError:
            value = self[item] = type(self)()
            return value

a = Example()

a[1][2][3] = 4
a[1][3][3] = 5
a[1][2]['test'] = 6

print a # {1: {2: {'test': 6, 3: 4}, 3: {3: 5}}}

  2. Array

  array模組定義了一個很像list的新物件型別,不同之處在於它限定了這個型別只能裝一種型別的元素。array元素的型別是在建立並使用的時候確定的。

  如果你的程式需要優化記憶體的使用,並且你確定你希望在list中儲存的資料都是同樣型別的,那麼使用array模組很合適。舉個例子,如果需要儲存一千萬個整數,如果用list,那麼你至少需要160MB的儲存空間,然而如果使用array,你只需要40MB。但雖然說能夠節省空間,array上幾乎沒有什麼基本操作能夠比在list上更快。

  在使用array進行計算的時候,需要特別注意那些建立list的操作。例如,使用列表推導式(list comprehension)的時候,會將array整個轉換為list,使得儲存空間膨脹。一個可行的替代方案是使用生成器表示式建立新的array。看程式碼:

import array

a = array.array("i", [1,2,3,4,5])
b = array.array(a.typecode, (2*x for x in a))

  因為使用array是為了節省空間,所以更傾向於使用in-place操作。一種更高效的方法是使用enumerate:

import array

a = array.array("i", [1,2,3,4,5])
for i, x in enumerate(a):
    a[i] = 2*x

  對於較大的array,這種in-place修改能夠比用生成器建立一個新的array至少提升15%的速度。

  那麼什麼時候使用array呢?是當你在考慮計算的因素之外,還需要得到一個像C語言裡一樣統一元素型別的陣列時。

import array
from timeit import Timer

def arraytest():
    a = array.array("i", [1, 2, 3, 4, 5])
    b = array.array(a.typecode, (2 * x for x in a))

def enumeratetest():
    a = array.array("i", [1, 2, 3, 4, 5])
    for i, x in enumerate(a):
        a[i] = 2 * x

if __name__=='__main__':
    m = Timer("arraytest()", "from __main__ import arraytest")
    n = Timer("enumeratetest()", "from __main__ import enumeratetest")

    print m.timeit() # 5.22479210582
    print n.timeit() # 4.34367196717

  3. Heapq

  heapq模組使用一個用堆實現的優先順序佇列。堆是一種簡單的有序列表,並且置入了堆的相關規則。

  堆是一種樹形的資料結構,樹上的子節點與父節點之間存在順序關係。二叉堆(binary heap)能夠用一個經過組織的列表或陣列結構來標識,在這種結構中,元素N的子節點的序號為2*N+1和2*N+2(下標始於0)。簡單來說,這個模組中的所有函式都假設序列是有序的,所以序列中的第一個元素(seq[0])是最小的,序列的其他部分構成一個二叉樹,並且seq[i]節點的子節點分別為seq[2*i+1]以及seq[2*i+2]。當對序列進行修改時,相關函式總是確保子節點大於等於父節點。

import heapq

heap = []

for value in [20, 10, 30, 50, 40]:
    heapq.heappush(heap, value)

while heap:
    print heapq.heappop(heap)

  heapq模組有兩個函式nlargest()和nsmallest(),顧名思義,讓我們來看看它們的用法。

import heapq

nums = [1, 8, 2, 23, 7, -4, 18, 23, 42, 37, 2]
print(heapq.nlargest(3, nums)) # Prints [42, 37, 23]
print(heapq.nsmallest(3, nums)) # Prints [-4, 1, 2]

  兩個函式也能夠通過一個鍵引數使用更為複雜的資料結構,例如:

import heapq

portfolio = [
{'name': 'IBM', 'shares': 100, 'price': 91.1},
{'name': 'AAPL', 'shares': 50, 'price': 543.22},
{'name': 'FB', 'shares': 200, 'price': 21.09},
{'name': 'HPQ', 'shares': 35, 'price': 31.75},
{'name': 'YHOO', 'shares': 45, 'price': 16.35},
{'name': 'ACME', 'shares': 75, 'price': 115.65}
]
cheap = heapq.nsmallest(3, portfolio, key=lambda s: s['price'])
expensive = heapq.nlargest(3, portfolio, key=lambda s: s['price'])

print cheap

# [{'price': 16.35, 'name': 'YHOO', 'shares': 45},
# {'price': 21.09, 'name': 'FB', 'shares': 200}, {'price': 31.75, 'name': 'HPQ', 'shares': 35}]

print expensive

# [{'price': 543.22, 'name': 'AAPL', 'shares': 50}, {'price': 115.65, 'name': 'ACME', 
# 'shares': 75}, {'price': 91.1, 'name': 'IBM', 'shares': 100}]

  來看看如何實現一個根據給定優先順序進行排序,並且每次pop操作都返回優先順序最高的元素的佇列例子。

import heapq

class Item:
    def __init__(self, name):
        self.name = name

    def __repr__(self):
        return 'Item({!r})'.format(self.name)

class PriorityQueue:
    def __init__(self):
        self._queue = []
        self._index = 0

    def push(self, item, priority):
        heapq.heappush(self._queue, (-priority, self._index, item))
        self._index += 1

    def pop(self):
        return heapq.heappop(self._queue)[-1]

q = PriorityQueue()
q.push(Item('foo'), 1)
q.push(Item('bar'), 5)
q.push(Item('spam'), 4)
q.push(Item('grok'), 1)

print q.pop() # Item('bar')
print q.pop() # Item('spam')
print q.pop() # Item('foo')
print q.pop() # Item('grok')

  4. Bisect

  bisect模組能夠提供保持list元素序列的支援。它使用了二分法完成大部分的工作。它在向一個list插入元素的同時維持list是有序的。在某些情況下,這比重複的對一個list進行排序更為高效,並且對於一個較大的list來說,對每步操作維持其有序也比對其排序要高效。

  假設你有一個range集合:

a = [(0, 100), (150, 220), (500, 1000)]

  如果我想新增一個range (250, 400),我可能會這麼做:

import bisect

a = [(0, 100), (150, 220), (500, 1000)]

bisect.insort_right(a, (250,400))

print a # [(0, 100), (150, 220), (250, 400), (500, 1000)]

  我們可以使用bisect()函式來尋找插入點:

import bisect

a = [(0, 100), (150, 220), (500, 1000)]

bisect.insort_right(a, (250,400))
bisect.insort_right(a, (399, 450))
print a # [(0, 100), (150, 220), (250, 400), (500, 1000)]

print bisect.bisect(a, (550, 1200)) # 5

  bisect(sequence, item) => index 返回元素應該的插入點,但序列並不被修改。

import bisect

a = [(0, 100), (150, 220), (500, 1000)]

bisect.insort_right(a, (250,400))
bisect.insort_right(a, (399, 450))
print a # [(0, 100), (150, 220), (250, 400), (500, 1000)]

print bisect.bisect(a, (550, 1200)) # 5
bisect.insort_right(a, (550, 1200))
print a # [(0, 100), (150, 220), (250, 400), (399, 450), (500, 1000), (550, 1200)]

  新元素被插入到第5的位置。

  5. Weakref

  weakref模組能夠幫助我們建立Python引用,卻不會阻止物件的銷燬操作。這一節包含了weak reference的基本用法,並且引入一個代理類。

  在開始之前,我們需要明白什麼是strong reference。strong reference是一個對物件的引用次數、生命週期以及銷燬時機產生影響的指標。strong reference如你所見,就是當你將一個物件賦值給一個變數的時候產生的:

>>> a = [1,2,3]
>>> b = a

  在這種情況下,這個列表有兩個strong reference,分別是a和b。在這兩個引用都被釋放之前,這個list不會被銷燬。

class Foo(object):
    def __init__(self):
        self.obj = None
        print 'created'

    def __del__(self):
        print 'destroyed'

    def show(self):
        print self.obj

    def store(self, obj):
        self.obj = obj

a = Foo() # created
b = a
del a
del b # destroyed

  Weak reference則是對物件的引用計數器不會產生影響。當一個物件存在weak reference時,並不會影響物件的撤銷。這就說,如果一個物件僅剩下weak reference,那麼它將會被銷燬。

  你可以使用weakref.ref函式來建立物件的weak reference。這個函式呼叫需要將一個strong reference作為第一個引數傳給函式,並且返回一個weak reference。

>>> import weakref
>>> a = Foo()
created
>>> b = weakref.ref(a)
>>> b

  一個臨時的strong reference可以從weak reference中建立,即是下例中的b():

>>> a == b() 
True
>>> b().show()
None

  請注意當我們刪除strong reference的時候,物件將立即被銷燬。

>>> del a
destroyed

  如果試圖在物件被摧毀之後通過weak reference使用物件,則會返回None:

>>> b() is None
True

  若是使用weakref.proxy,就能提供相對於weakref.ref更透明的可選操作。同樣是使用一個strong reference作為第一個引數並且返回一個weak reference,proxy更像是一個strong reference,但當物件不存在時會丟擲異常。

>>> a = Foo()
created
>>> b = weakref.proxy(a)
>>> b.store('fish')
>>> b.show()
fish
>>> del a
destroyed
>>> b.show()
Traceback (most recent call last):
  File "", line 1, in ?
ReferenceError: weakly-referenced object no longer exists

  完整的例子:

  引用計數器是由Python的垃圾回收器使用的,當一個物件的應用計數器變為0,則其將會被垃圾回收器回收。

  最好將weak reference用於開銷較大的物件,或避免迴圈引用(雖然垃圾回收器經常幹這種事情)。

import weakref
import gc

class MyObject(object):
    def my_method(self):
        print 'my_method was called!'

obj = MyObject()
r = weakref.ref(obj)

gc.collect()
assert r() is obj #r() allows you to access the object referenced: it's there.

obj = 1 #Let's change what obj references to
gc.collect()
assert r() is None #There is no object left: it was gc'ed.

  提示:只有library模組中定義的class instances、functions、methods、sets、frozen sets、files、generators、type objects和certain object types(例如sockets、arrays和regular expression patterns)支援weakref。內建函式以及大部分內建型別如lists、dictionaries、strings和numbers則不支援。

  6. Copy()

  通過shallow或deep copy語法提供複製物件的函式操作。

  shallow和deep copying的不同之處在於對於混合型物件的操作(混合物件是包含了其他型別物件的物件,例如list或其他類例項)。

  • 對於shallow copy而言,它建立一個新的混合物件,並且將原物件中其他物件的引用插入新物件。
  • 對於deep copy而言,它建立一個新的物件,並且遞迴地複製源物件中的其他物件並插入新的物件中。

  普通的賦值操作知識簡單的將心變數指向源物件。

import copy

a = [1,2,3]
b = [4,5]

c = [a,b]

# Normal Assignment
d = c

print id(c) == id(d)          # True - d is the same object as c
print id(c[0]) == id(d[0])    # True - d[0] is the same object as c[0]

# Shallow Copy
d = copy.copy(c)

print id(c) == id(d)          # False - d is now a new object
print id(c[0]) == id(d[0])    # True - d[0] is the same object as c[0]

# Deep Copy
d = copy.deepcopy(c)

print id(c) == id(d)          # False - d is now a new object
print id(c[0]) == id(d[0])    # False - d[0] is now a new object

  shallow copy (copy())操作建立一個新的容器,其包含的引用指向原物件中的物件。

  deep copy (deepcopy())建立的物件包含的引用指向複製出來的新物件。

  複雜的例子:

  假定我有兩個類,名為Manager和Graph,每個Graph包含了一個指向其manager的引用,而每個Manager有一個指向其管理的Graph的集合,現在我們有兩個任務需要完成:

  1) 複製一個graph例項,使用deepcopy,但其manager指向為原graph的manager。

  2) 複製一個manager,完全建立新manager,但拷貝原有的所有graph。

import weakref, copy

class Graph(object):
    def __init__(self, manager=None):
        self.manager = None if manager is None else weakref.ref(manager)
    def __deepcopy__(self, memodict):
        manager = self.manager()
        return Graph(memodict.get(id(manager), manager))

class Manager(object):
    def __init__(self, graphs=[]):
        self.graphs = graphs
        for g in self.graphs:
            g.manager = weakref.ref(self)

a = Manager([Graph(), Graph()])
b = copy.deepcopy(a)

if [g.manager() is b for g in b.graphs]:
    print True # True

if copy.deepcopy(a.graphs[0]).manager() is a:
    print True # True

  7. Pprint()

  Pprint模組能夠提供比較優雅的資料結構列印方式,如果你需要列印一個結構較為複雜,層次較深的字典或是JSON物件時,使用Pprint能夠提供較好的列印結果。

  假定你需要列印一個矩陣,當使用普通的print時,你只能列印出普通的列表,不過如果使用pprint,你就能打出漂亮的矩陣結構

  如果

import pprint

matrix = [ [1,2,3], [4,5,6], [7,8,9] ]
a = pprint.PrettyPrinter(width=20)
a.pprint(matrix)

# [[1, 2, 3],
#  [4, 5, 6],
#  [7, 8, 9]]

  額外的知識

  一些基本的資料結構

  1. 單鏈連結串列

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

    def set_and_return_Next(self):
        self.nextNode = Node()
        return self.nextNode

    def getNext(self):
        return self.nextNode

    def getData(self):
        return self.data

    def setData(self, d):
        self.data = d

class LinkedList:
    def buildList(self, array):
        self.head = Node()
        self.head.setData(array[0])
        self.temp = self.head
        for i in array[1:]:
            self.temp = self.temp.set_and_return_Next()
            self.temp.setData(i)
            self.tail = self.temp
        return self.head
    def printList(self):
        tempNode = self.head
        while(tempNode!=self.tail):
            print(tempNode.getData())
            tempNode = tempNode.getNext()
        print(self.tail.getData())
myArray = [3, 5, 4, 6, 2, 6, 7, 8, 9, 10, 21]

myList = LinkedList()
myList.buildList(myArray)
myList.printList()

  2. 用Python實現的普林姆演算法

  譯者注:普林姆演算法(Prims Algorithm)是圖論中,在加權連通圖中搜尋最小生成樹的演算法。

from collections import defaultdict
from heapq import heapify, heappop, heappush

def prim( nodes, edges ):
    conn = defaultdict( list )
    for n1,n2,c in edges:
        conn[ n1 ].append( (c, n1, n2) )
        conn[ n2 ].append( (c, n2, n1) )

    mst = []
    used = set( nodes[ 0 ] )
    usable_edges = conn[ nodes[0] ][:]
    heapify( usable_edges )

    while usable_edges:
        cost, n1, n2 = heappop( usable_edges )
        if n2 not in used:
            used.add( n2 )
            mst.append( ( n1, n2, cost ) )

            for e in conn[ n2 ]:
                if e[ 2 ] not in used:
                    heappush( usable_edges, e )
    return mst

#test
nodes = list("ABCDEFG")
edges = [ ("A", "B", 7), ("A", "D", 5),
          ("B", "C", 8), ("B", "D", 9), ("B", "E", 7),
      ("C", "E", 5),
      ("D", "E", 15), ("D", "F", 6),
      ("E", "F", 8), ("E", "G", 9),
      ("F", "G", 11)]

print "prim:", prim( nodes, edges )

  總結

  如果想了解更多地資料結構資訊請參閱相關文件。謝謝閱讀。

  原文連結: pypix.com   翻譯: 伯樂線上 - 熊崽Kevin

相關文章