It’s been a week since last time I read Python Algorithms ,time flies. There are always so many errands keeping me from reading a tech book constantly.
I think keeping notes in blog helps drive me to continue .
One thing bothers me though ,I’m not sure about the language mistakes I made in blogs ,which I believe must be a lot.
I dont’t know how to check out all my mistakes to improve.
And I might end up writing my own ‘freestyle’ English ,without any real progress.
Anyway,today I’m reading about Graphs anf Trees.
1.Graphs
1.1 Basic Concepts
Graphs are one of the most powerful frameworks in algorithms.
Graphs can represent all kinds of strctures and systems we may encounter solving algorithmic problems.
A graph G=(V,E) consists of a set of nodes ,V, and edges E between them.
If the edges have a direction ,we say the graph is directed.
If we associate a weight with each edge in G , we say G is a weighted graph
A forest is a cycle-free graph, a connected graph is a tree
BLACK BOX
Hashing is used in the machanism of dictionaries and sets
Accessing elements of a dict or set can be assumed to take constant time ,basically,Θ(1) in average
1.2 Implementing of graphs
(1)Adjacency lists and the like
Basically, for each node,
we can access a list (or set or other container or iterable) of its neighbors。
Let’s take the simplest way of implementing this, assuming we have n nodes, numbered 0…n–1.
Method1. A straightforward Adjaceny Set Representation
a,b,c,d,e,f,g,h = range(5)
N = [
{b, c, d, e, f}, # a
{c, e}, # b
{d}, # c
{e}, # d
{f}, # e
{c, g, h}, # f
{f, h}, # g
{f, g} # h
]
N[v] is now a set of v’s neighbors.
>>>b in N[a] # Neighborhood membership
True
>>> len(N[f]) # Degree
3
Method 2. Adjacency Lists
a, b, c, d, e, f, g, h = range(8)
N = [
[b, c, d, e, f], # a
[c, e], # b
[d], # c
[e], # d
[f], # e
[c, g, h], # f
[f, h], # g
[f, g] # h
]
Replace the adjacency sets with adjacency lists.
The membership checking is now Θ(n),which is a significant slow down.
But it’s useful and neccessary if we need to iterate over neighbors and the like.
Method3.Adjacency dicts with Edge Weights
a, b, c, d, e, f, g, h = range(8)
N = [
{b:2, c:1, d:3, e:9, f:4}, # a
{c:4, e:3}, # b
{d:8}, # c
{e:7}, # d
{f:5}, # e
{c:2, g:2, h:2}, # f
{f:1, h:6}, # g
{f:9, g:8} # h
]
The adjacency dict version can be used just like the others, with the additional edge weight functionality :
>>> b in N[a] # Neighborhood membership
True
>>> len(N[f]) # Degree
3
>>> N[a][b] # Edge weight for (a, b)
2
Method4.A Dict with Adjacency Sets
A more flexible approach (allowing us to use arbitrary,hashable, node labels) is to use a dict as this main structure.
N = {
'a': set('bcdef'),
'b': set('ce'),
'c': set('d'),
'd': set('e'),
'e': set('f'),
'f': set('cgh'),
'g': set('fh'),
'h': set('fg')
}
Note that nodes are now represented by characters.
(2)Adjacency Matrices
Instead of listing all neighbors for each node, we have one row (an array) with one position for each possible neighbor (that is, one for each node in the graph), and store a value (such as True or False),indicating whether that node is indeed a neighbor.
Method1.An Adjacency Matrix, Implemented with Nested Lists
a, b, c, d, e, f, g, h = range(8)
# a b c d e f g h
N = [[0,1,1,1,1,1,0,0], # a
[0,0,1,0,1,0,0,0], # b
[0,0,0,1,0,0,0,0], # c
[0,0,0,0,1,0,0,0], # d
[0,0,0,0,0,1,0,0], # e
[0,0,1,0,0,0,1,1], # f
[0,0,0,0,0,1,0,1], # g
[0,0,0,0,0,1,1,0]] # h
The way we’d use this is slightly different from the adjacency lists/sets.
>>> N[a][b] # Neighborhood membership
1
>>> sum(N[f]) # Degree
3
Method2.A Weight Matrix with Infinite Weight for Missing Edges
a, b, c, d, e, f, g, h = range(8)
_ = float('inf')
# a b c d e f g h
W = [[0,2,1,3,9,4,_,_], # a
[_,0,4,_,3,_,_,_], # b
[_,_,0,8,_,_,_,_], # c
[_,_,_,0,7,_,_,_], # d
[_,_,_,_,0,5,_,_], # e
[_,_,2,_,_,0,2,2], # f
[_,_,_,_,_,1,0,6], # g
[_,_,_,_,_,9,8,0]] # h
Weight matrices make it easy to access edge weights, of course, but membership checking and finding the degree of a node, for example, or even iterating over neighbors must be done a bit differently now. You need to take the infinity value into account–for example, like this (using inf = float(‘inf’) for more readable code)
>>> W[a][b] < inf # Neighborhood membership
True
>>> W[c][e] < inf # Neighborhood membership
False
>>> sum(1 for w in W[a] if w < inf) - 1 # Degree
5Note that 1 is subtracted from the degree sum, because we don’t want to count the diagonal.
Thedegree calculation here is Θ(n), whereas both membership and degree could easily be found in constant time with the proper structure.
Again, you should always keep in mind how you are going to use your graph and represent it accordingly
2.Trees
Trees are just a special kind of graphs, so most algorithms and
representations for graphs will work for them as well.
However, because of their special properties (they are connected and have no cycles),
some specialized (and quite simple) versions of both the representations and algorithms are possible.
2.1 Implementing trees
Method1.represent that tree with lists of lists
>>> T = [[“a”, “b”], [“c”], [“d”, [“e”, “f”]]]
>>> T[0][1]
‘b’
>>> T[2][1][0]
‘e’
Each list is, in a way, a neighbor (or child) list of the (anonymous) internal nodes.
In the second example, we access the third child of the root, the second child of that child, and finally the first child of that (path highlighted in the figure).
Method2.A Binary Tree Class
A binary tree is one where each internal node has a maximum of two children.
class Tree:
def __init__(self, left, right):
self.left = left
self.right = right
You can use the Tree class like this:
>>> t = Tree(Tree(“a”, “b”), Tree(“c”, “d”))
>>> t.right.left
‘c
Method3.A Multiway Tree Class
A common way of implementing trees, especially in languages that don’t have built-in lists, is the first child, next sibling representation.
Each tree node refers to a linked list of siblings (its children), and each of these siblings refers to a linked list of its own.
class Tree:
def __init__(self, kids, next=None):
self.kids = self.val = kids
self.next = next
Here’s an example of how you can access this structure:
>>> t = Tree(Tree(“a”, Tree(“b”, Tree(“c”, Tree(“d”)))))
>>> t.kids.next.next.val
‘c’
And here’s what that tree looks like:
Postscript
I copied a lot directly from the book. And I feel it’s really not easy to summarize my own unique conclusion with this kind of mature topic.
I guess I should concentrate more on the specific implemention methods , and ignore those theoretical details .I don’t really have that much time.
Fighting~!
Reference
《Python Algorithms》by Magnus Lie Hetland.

本文探讨了图和树这两种核心数据结构的不同实现方法。针对图的表示方式,包括邻接表、邻接集及邻接矩阵等进行了详细介绍,并讨论了它们各自的优缺点。对于树的实现,则涵盖了列表表示法、二叉树类和多路树类等多种形式。此外,还对比了不同表示方法在实际应用中的适用场景。
479

被折叠的 条评论
为什么被折叠?



