Tôi đang cố gắng xây dựng một cây chung.
Có bất kỳ cấu trúc dữ liệu tích hợp nào trong Python để thực hiện nó không?
Tôi đang cố gắng xây dựng một cây chung.
Có bất kỳ cấu trúc dữ liệu tích hợp nào trong Python để thực hiện nó không?
Câu trả lời:
Tôi khuyên bạn nên https://pypi.python.org/pypi/anytree (Tôi là tác giả)
from anytree import Node, RenderTree
udo = Node("Udo")
marc = Node("Marc", parent=udo)
lian = Node("Lian", parent=marc)
dan = Node("Dan", parent=udo)
jet = Node("Jet", parent=dan)
jan = Node("Jan", parent=dan)
joe = Node("Joe", parent=dan)
print(udo)
Node('/Udo')
print(joe)
Node('/Udo/Dan/Joe')
for pre, fill, node in RenderTree(udo):
print("%s%s" % (pre, node.name))
Udo
├── Marc
│ └── Lian
└── Dan
├── Jet
├── Jan
└── Joe
print(dan.children)
(Node('/Udo/Dan/Jet'), Node('/Udo/Dan/Jan'), Node('/Udo/Dan/Joe'))
anytree cũng có một API mạnh mẽ với:
anytree
có lẽ là một thư viện tuyệt vời, đây là một câu hỏi python, không phải là một câu hỏi Node.js.
Python không có phạm vi cấu trúc dữ liệu "tích hợp" khá rộng như Java. Tuy nhiên, vì Python là động, nên một cây nói chung rất dễ tạo. Ví dụ: cây nhị phân có thể là:
class Tree:
def __init__(self):
self.left = None
self.right = None
self.data = None
Bạn có thể sử dụng nó như thế này:
root = Tree()
root.data = "root"
root.left = Tree()
root.left.data = "left"
root.right = Tree()
root.right.data = "right"
class Tree
từ đối tượng
object
đôi khi chỉ là một hướng dẫn: Nếu một lớp kế thừa từ không có lớp cơ sở nào khác, thì kế thừa rõ ràng từ đối tượng. Điều này cũng áp dụng cho các lớp lồng nhau. Xem Hướng dẫn về Phong cách Python của Google
Cây chung là một nút có 0 hoặc nhiều con, mỗi nút là một nút (cây) thích hợp. Nó không giống như cây nhị phân, chúng là các cấu trúc dữ liệu khác nhau, mặc dù cả hai đều có chung một số thuật ngữ.
Không có bất kỳ cấu trúc dữ liệu dựng sẵn nào cho các cây chung trong Python, nhưng nó dễ dàng được thực hiện với các lớp.
class Tree(object):
"Generic tree node."
def __init__(self, name='root', children=None):
self.name = name
self.children = []
if children is not None:
for child in children:
self.add_child(child)
def __repr__(self):
return self.name
def add_child(self, node):
assert isinstance(node, Tree)
self.children.append(node)
# *
# /|\
# 1 2 +
# / \
# 3 4
t = Tree('*', [Tree('1'),
Tree('2'),
Tree('+', [Tree('3'),
Tree('4')])])
Bạn co thể thử:
from collections import defaultdict
def tree(): return defaultdict(tree)
users = tree()
users['harold']['username'] = 'hrldcpr'
users['handler']['username'] = 'matthandlersux'
Theo đề xuất ở đây: https://gist.github.com/2012250
class Node:
"""
Class Node
"""
def __init__(self, value):
self.left = None
self.data = value
self.right = None
class Tree:
"""
Class tree will provide a tree as well as utility functions.
"""
def createNode(self, data):
"""
Utility function to create a node.
"""
return Node(data)
def insert(self, node , data):
"""
Insert function will insert a node into tree.
Duplicate keys are not allowed.
"""
#if tree is empty , return a root node
if node is None:
return self.createNode(data)
# if data is smaller than parent , insert it into left side
if data < node.data:
node.left = self.insert(node.left, data)
elif data > node.data:
node.right = self.insert(node.right, data)
return node
def search(self, node, data):
"""
Search function will search a node into tree.
"""
# if root is None or root is the search data.
if node is None or node.data == data:
return node
if node.data < data:
return self.search(node.right, data)
else:
return self.search(node.left, data)
def deleteNode(self,node,data):
"""
Delete function will delete a node into tree.
Not complete , may need some more scenarion that we can handle
Now it is handling only leaf.
"""
# Check if tree is empty.
if node is None:
return None
# searching key into BST.
if data < node.data:
node.left = self.deleteNode(node.left, data)
elif data > node.data:
node.right = self.deleteNode(node.right, data)
else: # reach to the node that need to delete from BST.
if node.left is None and node.right is None:
del node
if node.left == None:
temp = node.right
del node
return temp
elif node.right == None:
temp = node.left
del node
return temp
return node
def traverseInorder(self, root):
"""
traverse function will print all the node in the tree.
"""
if root is not None:
self.traverseInorder(root.left)
print root.data
self.traverseInorder(root.right)
def traversePreorder(self, root):
"""
traverse function will print all the node in the tree.
"""
if root is not None:
print root.data
self.traversePreorder(root.left)
self.traversePreorder(root.right)
def traversePostorder(self, root):
"""
traverse function will print all the node in the tree.
"""
if root is not None:
self.traversePostorder(root.left)
self.traversePostorder(root.right)
print root.data
def main():
root = None
tree = Tree()
root = tree.insert(root, 10)
print root
tree.insert(root, 20)
tree.insert(root, 30)
tree.insert(root, 40)
tree.insert(root, 70)
tree.insert(root, 60)
tree.insert(root, 80)
print "Traverse Inorder"
tree.traverseInorder(root)
print "Traverse Preorder"
tree.traversePreorder(root)
print "Traverse Postorder"
tree.traversePostorder(root)
if __name__ == "__main__":
main()
Không có cây nào được xây dựng, nhưng bạn có thể dễ dàng xây dựng một cây bằng cách phân loại một loại Nút từ Danh sách và viết các phương thức truyền tải. Nếu bạn làm điều này, tôi đã thấy chia đôi hữu ích.
Ngoài ra còn có nhiều triển khai trên PyPi mà bạn có thể duyệt.
Nếu tôi nhớ chính xác, lib tiêu chuẩn Python không bao gồm các cấu trúc dữ liệu cây vì lý do tương tự mà thư viện lớp cơ sở .NET không: địa phương của bộ nhớ bị giảm, dẫn đến việc bỏ lỡ bộ nhớ cache nhiều hơn. Trên các bộ xử lý hiện đại, thường nhanh hơn khi chỉ mang một khối lớn bộ nhớ vào bộ đệm và cấu trúc dữ liệu "giàu con trỏ" phủ nhận lợi ích.
Tôi thực hiện một cây gốc như một từ điển {child:parent}
. Vì vậy, ví dụ với nút gốc 0
, một cây có thể trông như thế:
tree={1:0, 2:0, 3:1, 4:2, 5:3}
Cấu trúc này làm cho nó khá dễ dàng để đi lên dọc theo một đường dẫn từ bất kỳ nút nào đến gốc, có liên quan đến vấn đề tôi đang làm việc.
{parent:[leftchild,rightchild]}
.
Câu trả lời của Greg Hewgill là tuyệt vời nhưng nếu bạn cần nhiều nút hơn cho mỗi cấp độ, bạn có thể sử dụng danh sách | từ điển để tạo chúng: Và sau đó sử dụng phương pháp để truy cập chúng theo tên hoặc theo thứ tự (như id)
class node(object):
def __init__(self):
self.name=None
self.node=[]
self.otherInfo = None
self.prev=None
def nex(self,child):
"Gets a node by number"
return self.node[child]
def prev(self):
return self.prev
def goto(self,data):
"Gets the node by name"
for child in range(0,len(self.node)):
if(self.node[child].name==data):
return self.node[child]
def add(self):
node1=node()
self.node.append(node1)
node1.prev=self
return node1
Bây giờ chỉ cần tạo một root và xây dựng nó lên: ex:
tree=node() #create a node
tree.name="root" #name it root
tree.otherInfo="blue" #or what ever
tree=tree.add() #add a node to the root
tree.name="node1" #name it
root
/
child1
tree=tree.add()
tree.name="grandchild1"
root
/
child1
/
grandchild1
tree=tree.prev()
tree=tree.add()
tree.name="gchild2"
root
/
child1
/ \
grandchild1 gchild2
tree=tree.prev()
tree=tree.prev()
tree=tree.add()
tree=tree.name="child2"
root
/ \
child1 child2
/ \
grandchild1 gchild2
tree=tree.prev()
tree=tree.goto("child1") or tree=tree.nex(0)
tree.name="changed"
root
/ \
changed child2
/ \
grandchild1 gchild2
Điều đó là đủ để bạn bắt đầu tìm ra cách để làm cho công việc này
class Tree(dict):
"""A tree implementation using python's autovivification feature."""
def __missing__(self, key):
value = self[key] = type(self)()
return value
#cast a (nested) dict to a (nested) Tree class
def __init__(self, data={}):
for k, data in data.items():
if isinstance(data, dict):
self[k] = type(self)(data)
else:
self[k] = data
hoạt động như một từ điển, nhưng cung cấp nhiều ký tự lồng nhau mà bạn muốn. Hãy thử như sau:
your_tree = Tree()
your_tree['a']['1']['x'] = '@'
your_tree['a']['1']['y'] = '#'
your_tree['a']['2']['x'] = '$'
your_tree['a']['3'] = '%'
your_tree['b'] = '*'
sẽ cung cấp một dict lồng nhau ... hoạt động như một cái cây thực sự.
{'a': {'1': {'x': '@', 'y': '#'}, '2': {'x': '$'}, '3': '%'}, 'b': '*'}
... Nếu bạn đã có một lệnh, nó sẽ chuyển mỗi cấp thành một cây:
d = {'foo': {'amy': {'what': 'runs'} } }
tree = Tree(d)
print(d['foo']['amy']['what']) # returns 'runs'
d['foo']['amy']['when'] = 'now' # add new branch
Theo cách này, bạn có thể tiếp tục chỉnh sửa / thêm / xóa từng cấp chính tả nếu muốn. Tất cả các phương pháp dict cho traversal vv, vẫn được áp dụng.
dict
thay vì defaultdict
? Từ các thử nghiệm của tôi, mở rộng defaultdict
thay vì dict và sau đó thêm self.default_factory = type(self)
vào đầu init sẽ hoạt động theo cùng một cách.
Tôi đã thực hiện cây bằng cách sử dụng các dicts lồng nhau. Nó khá dễ thực hiện và nó đã làm việc với tôi với các tập dữ liệu khá lớn. Tôi đã đăng một mẫu bên dưới và bạn có thể xem thêm tại mã Google
def addBallotToTree(self, tree, ballotIndex, ballot=""):
"""Add one ballot to the tree.
The root of the tree is a dictionary that has as keys the indicies of all
continuing and winning candidates. For each candidate, the value is also
a dictionary, and the keys of that dictionary include "n" and "bi".
tree[c]["n"] is the number of ballots that rank candidate c first.
tree[c]["bi"] is a list of ballot indices where the ballots rank c first.
If candidate c is a winning candidate, then that portion of the tree is
expanded to indicate the breakdown of the subsequently ranked candidates.
In this situation, additional keys are added to the tree[c] dictionary
corresponding to subsequently ranked candidates.
tree[c]["n"] is the number of ballots that rank candidate c first.
tree[c]["bi"] is a list of ballot indices where the ballots rank c first.
tree[c][d]["n"] is the number of ballots that rank c first and d second.
tree[c][d]["bi"] is a list of the corresponding ballot indices.
Where the second ranked candidates is also a winner, then the tree is
expanded to the next level.
Losing candidates are ignored and treated as if they do not appear on the
ballots. For example, tree[c][d]["n"] is the total number of ballots
where candidate c is the first non-losing candidate, c is a winner, and
d is the next non-losing candidate. This will include the following
ballots, where x represents a losing candidate:
[c d]
[x c d]
[c x d]
[x c x x d]
During the count, the tree is dynamically updated as candidates change
their status. The parameter "tree" to this method may be the root of the
tree or may be a sub-tree.
"""
if ballot == "":
# Add the complete ballot to the tree
weight, ballot = self.b.getWeightedBallot(ballotIndex)
else:
# When ballot is not "", we are adding a truncated ballot to the tree,
# because a higher-ranked candidate is a winner.
weight = self.b.getWeight(ballotIndex)
# Get the top choice among candidates still in the running
# Note that we can't use Ballots.getTopChoiceFromWeightedBallot since
# we are looking for the top choice over a truncated ballot.
for c in ballot:
if c in self.continuing | self.winners:
break # c is the top choice so stop
else:
c = None # no candidates left on this ballot
if c is None:
# This will happen if the ballot contains only winning and losing
# candidates. The ballot index will not need to be transferred
# again so it can be thrown away.
return
# Create space if necessary.
if not tree.has_key(c):
tree[c] = {}
tree[c]["n"] = 0
tree[c]["bi"] = []
tree[c]["n"] += weight
if c in self.winners:
# Because candidate is a winner, a portion of the ballot goes to
# the next candidate. Pass on a truncated ballot so that the same
# candidate doesn't get counted twice.
i = ballot.index(c)
ballot2 = ballot[i+1:]
self.addBallotToTree(tree[c], ballotIndex, ballot2)
else:
# Candidate is in continuing so we stop here.
tree[c]["bi"].append(ballotIndex)
Tôi đã xuất bản một triển khai cây Python [3] trên trang web của mình: http://www.quesucede.com/page/show/id/python_3_tree_imâyation .
Hy vọng nó được sử dụng,
Ok, đây là mã:
import uuid
def sanitize_id(id):
return id.strip().replace(" ", "")
(_ADD, _DELETE, _INSERT) = range(3)
(_ROOT, _DEPTH, _WIDTH) = range(3)
class Node:
def __init__(self, name, identifier=None, expanded=True):
self.__identifier = (str(uuid.uuid1()) if identifier is None else
sanitize_id(str(identifier)))
self.name = name
self.expanded = expanded
self.__bpointer = None
self.__fpointer = []
@property
def identifier(self):
return self.__identifier
@property
def bpointer(self):
return self.__bpointer
@bpointer.setter
def bpointer(self, value):
if value is not None:
self.__bpointer = sanitize_id(value)
@property
def fpointer(self):
return self.__fpointer
def update_fpointer(self, identifier, mode=_ADD):
if mode is _ADD:
self.__fpointer.append(sanitize_id(identifier))
elif mode is _DELETE:
self.__fpointer.remove(sanitize_id(identifier))
elif mode is _INSERT:
self.__fpointer = [sanitize_id(identifier)]
class Tree:
def __init__(self):
self.nodes = []
def get_index(self, position):
for index, node in enumerate(self.nodes):
if node.identifier == position:
break
return index
def create_node(self, name, identifier=None, parent=None):
node = Node(name, identifier)
self.nodes.append(node)
self.__update_fpointer(parent, node.identifier, _ADD)
node.bpointer = parent
return node
def show(self, position, level=_ROOT):
queue = self[position].fpointer
if level == _ROOT:
print("{0} [{1}]".format(self[position].name,
self[position].identifier))
else:
print("\t"*level, "{0} [{1}]".format(self[position].name,
self[position].identifier))
if self[position].expanded:
level += 1
for element in queue:
self.show(element, level) # recursive call
def expand_tree(self, position, mode=_DEPTH):
# Python generator. Loosly based on an algorithm from 'Essential LISP' by
# John R. Anderson, Albert T. Corbett, and Brian J. Reiser, page 239-241
yield position
queue = self[position].fpointer
while queue:
yield queue[0]
expansion = self[queue[0]].fpointer
if mode is _DEPTH:
queue = expansion + queue[1:] # depth-first
elif mode is _WIDTH:
queue = queue[1:] + expansion # width-first
def is_branch(self, position):
return self[position].fpointer
def __update_fpointer(self, position, identifier, mode):
if position is None:
return
else:
self[position].update_fpointer(identifier, mode)
def __update_bpointer(self, position, identifier):
self[position].bpointer = identifier
def __getitem__(self, key):
return self.nodes[self.get_index(key)]
def __setitem__(self, key, item):
self.nodes[self.get_index(key)] = item
def __len__(self):
return len(self.nodes)
def __contains__(self, identifier):
return [node.identifier for node in self.nodes
if node.identifier is identifier]
if __name__ == "__main__":
tree = Tree()
tree.create_node("Harry", "harry") # root node
tree.create_node("Jane", "jane", parent = "harry")
tree.create_node("Bill", "bill", parent = "harry")
tree.create_node("Joe", "joe", parent = "jane")
tree.create_node("Diane", "diane", parent = "jane")
tree.create_node("George", "george", parent = "diane")
tree.create_node("Mary", "mary", parent = "diane")
tree.create_node("Jill", "jill", parent = "george")
tree.create_node("Carol", "carol", parent = "jill")
tree.create_node("Grace", "grace", parent = "bill")
tree.create_node("Mark", "mark", parent = "jane")
print("="*80)
tree.show("harry")
print("="*80)
for node in tree.expand_tree("harry", mode=_WIDTH):
print(node)
print("="*80)
Nếu ai đó cần một cách đơn giản hơn để làm điều đó, một cây chỉ là một danh sách lồng nhau đệ quy (vì tập hợp không thể băm được):
[root, [child_1, [[child_11, []], [child_12, []]], [child_2, []]]]
Trong đó mỗi nhánh là một cặp: [ object, [children] ]
và mỗi lá là một cặp:[ object, [] ]
Nhưng nếu bạn cần một lớp với các phương thức, bạn có thể sử dụng anytree.
Những hoạt động bạn cần? Thường có một giải pháp tốt trong Python bằng cách sử dụng dict hoặc danh sách với mô-đun bisect.
Có rất nhiều, rất nhiều cây thực hiện trên PyPI và nhiều loại cây gần như không quan trọng để tự thực hiện trong Python thuần túy. Tuy nhiên, điều này hiếm khi cần thiết.
Một cách thực hiện cây khác dựa trên câu trả lời của Bruno :
class Node:
def __init__(self):
self.name: str = ''
self.children: List[Node] = []
self.parent: Node = self
def __getitem__(self, i: int) -> 'Node':
return self.children[i]
def add_child(self):
child = Node()
self.children.append(child)
child.parent = self
return child
def __str__(self) -> str:
def _get_character(x, left, right) -> str:
if x < left:
return '/'
elif x >= right:
return '\\'
else:
return '|'
if len(self.children):
children_lines: Sequence[List[str]] = list(map(lambda child: str(child).split('\n'), self.children))
widths: Sequence[int] = list(map(lambda child_lines: len(child_lines[0]), children_lines))
max_height: int = max(map(len, children_lines))
total_width: int = sum(widths) + len(widths) - 1
left: int = (total_width - len(self.name) + 1) // 2
right: int = left + len(self.name)
return '\n'.join((
self.name.center(total_width),
' '.join(map(lambda width, position: _get_character(position - width // 2, left, right).center(width),
widths, accumulate(widths, add))),
*map(
lambda row: ' '.join(map(
lambda child_lines: child_lines[row] if row < len(child_lines) else ' ' * len(child_lines[0]),
children_lines)),
range(max_height))))
else:
return self.name
Và một ví dụ về cách sử dụng nó:
tree = Node()
tree.name = 'Root node'
tree.add_child()
tree[0].name = 'Child node 0'
tree.add_child()
tree[1].name = 'Child node 1'
tree.add_child()
tree[2].name = 'Child node 2'
tree[1].add_child()
tree[1][0].name = 'Grandchild 1.0'
tree[2].add_child()
tree[2][0].name = 'Grandchild 2.0'
tree[2].add_child()
tree[2][1].name = 'Grandchild 2.1'
print(tree)
Mà nên xuất:
Nút gốc / / \ Nút con 0 Nút con 1 Nút con 2 | / \ Cháu 1.0 Cháu 2.0 Cháu 2.1 Cháu 2.1
Tôi đề nghị thư viện networkx .
NetworkX là gói Python để tạo, thao tác và nghiên cứu cấu trúc, động lực và chức năng của các mạng phức tạp.
Một ví dụ về xây dựng cây:
import networkx as nx
G = nx.Graph()
G.add_edge('A', 'B')
G.add_edge('B', 'C')
G.add_edge('B', 'D')
G.add_edge('A', 'E')
G.add_edge('E', 'F')
Tôi không chắc ý của bạn về " Cây chung " là gì,
nhưng thư viện cho phép mỗi nút trở thành bất kỳ đối tượng có thể băm nào và không có ràng buộc nào về số lượng con mà mỗi nút có.
Thư viện cũng cung cấp các thuật toán đồ thị liên quan đến cây và khả năng hiển thị .
Nếu bạn muốn tạo cấu trúc dữ liệu cây thì trước tiên bạn phải tạo đối tượng treeEuity. Nếu bạn tạo đối tượng treeEuity, thì bạn có thể quyết định cách cây của bạn cư xử.
Để làm điều này sau đây là lớp TreeEuity:
class TreeElement (object):
def __init__(self):
self.elementName = None
self.element = []
self.previous = None
self.elementScore = None
self.elementParent = None
self.elementPath = []
self.treeLevel = 0
def goto(self, data):
for child in range(0, len(self.element)):
if (self.element[child].elementName == data):
return self.element[child]
def add(self):
single_element = TreeElement()
single_element.elementName = self.elementName
single_element.previous = self.elementParent
single_element.elementScore = self.elementScore
single_element.elementPath = self.elementPath
single_element.treeLevel = self.treeLevel
self.element.append(single_element)
return single_element
Bây giờ, chúng ta phải sử dụng phần tử này để tạo cây, tôi đang sử dụng cây A * trong ví dụ này.
class AStarAgent(Agent):
# Initialization Function: Called one time when the game starts
def registerInitialState(self, state):
return;
# GetAction Function: Called with every frame
def getAction(self, state):
# Sorting function for the queue
def sortByHeuristic(each_element):
if each_element.elementScore:
individual_score = each_element.elementScore[0][0] + each_element.treeLevel
else:
individual_score = admissibleHeuristic(each_element)
return individual_score
# check the game is over or not
if state.isWin():
print('Job is done')
return Directions.STOP
elif state.isLose():
print('you lost')
return Directions.STOP
# Create empty list for the next states
astar_queue = []
astar_leaf_queue = []
astar_tree_level = 0
parent_tree_level = 0
# Create Tree from the give node element
astar_tree = TreeElement()
astar_tree.elementName = state
astar_tree.treeLevel = astar_tree_level
astar_tree = astar_tree.add()
# Add first element into the queue
astar_queue.append(astar_tree)
# Traverse all the elements of the queue
while astar_queue:
# Sort the element from the queue
if len(astar_queue) > 1:
astar_queue.sort(key=lambda x: sortByHeuristic(x))
# Get the first node from the queue
astar_child_object = astar_queue.pop(0)
astar_child_state = astar_child_object.elementName
# get all legal actions for the current node
current_actions = astar_child_state.getLegalPacmanActions()
if current_actions:
# get all the successor state for these actions
for action in current_actions:
# Get the successor of the current node
next_state = astar_child_state.generatePacmanSuccessor(action)
if next_state:
# evaluate the successor states using scoreEvaluation heuristic
element_scored = [(admissibleHeuristic(next_state), action)]
# Increase the level for the child
parent_tree_level = astar_tree.goto(astar_child_state)
if parent_tree_level:
astar_tree_level = parent_tree_level.treeLevel + 1
else:
astar_tree_level += 1
# create tree for the finding the data
astar_tree.elementName = next_state
astar_tree.elementParent = astar_child_state
astar_tree.elementScore = element_scored
astar_tree.elementPath.append(astar_child_state)
astar_tree.treeLevel = astar_tree_level
astar_object = astar_tree.add()
# If the state exists then add that to the queue
astar_queue.append(astar_object)
else:
# Update the value leaf into the queue
astar_leaf_state = astar_tree.goto(astar_child_state)
astar_leaf_queue.append(astar_leaf_state)
Bạn có thể thêm / xóa bất kỳ phần tử nào khỏi đối tượng, nhưng làm cho cấu trúc trở nên phức tạp.
def iterative_bfs(graph, start):
'''iterative breadth first search from start'''
bfs_tree = {start: {"parents":[], "children":[], "level":0}}
q = [start]
while q:
current = q.pop(0)
for v in graph[current]:
if not v in bfs_tree:
bfs_tree[v]={"parents":[current], "children":[], "level": bfs_tree[current]["level"] + 1}
bfs_tree[current]["children"].append(v)
q.append(v)
else:
if bfs_tree[v]["level"] > bfs_tree[current]["level"]:
bfs_tree[current]["children"].append(v)
bfs_tree[v]["parents"].append(current)