python数据结构与算法 37 树的实现

树的实现

记住上一节树的定义,在定义的基础上,我们用以下的函数创建并操作二叉树:

  • BinaryTree() 创建一个二叉树实例
  • getLeftChild() 返回节点的左孩子
  • getRightChild() 返回节点的右孩子
  • setRootVal(val) 把val变量值赋给当前节点
  • getRootVal() 返回当前节点对象。
  • insertLeft(val) 创建一个新二叉树作为当前节点的左孩子
  • insertRight(val) 创建一个新二叉树作为当前节点的右孩子。

实现树的关键点是合适的存储技术。Python提供两种可用方法,所以在做出决定之前,先查验一下两种技术。第一种叫“列表的列表”,第二种叫“节点与引用”

列表的列表表示法

在列表的列表方法中,我们使用列表数据结构来实现上面的函数,虽然这种操作方法与我们以前的抽象数据结构很不相同,不过它很有意思的一点是,这是一个简单的递归结构,我们可以直接查看和检查。

在列表的列表树中,根节点的数值是列表的第一个元素,第二个元素是它的左子树,第三个元素就是它的右子树。为说明这个存储结构,我们看一个例子。图1所示为一个如此实现的简单树。

图1 一棵小树

myTree = [‘a‘,   #root
      [‘b‘,  #left subtree
       [‘d‘ [], []],
       [‘e‘ [], []] ],
      [‘c‘,  #right subtree
       [‘f‘ [], []],
       [] ]
     ]

有没有发现我们可以直接用列表的索引来访问子树。树根是myTree[0],左子树是myTree[1],右子树是myTree[2]。下面的代码就是用列表创建了一棵树,完成之后,就可以访问它的根和子树,它的好处在于列表中的“元素列表”就代表了子树,与树有相同的结构,所以它的结构是递归的。如果一个子树有根节点,但是左右子树都是空列表,那么它就是叶子。另一个好处是这种方法产生的树可以推广到“多叉树”而不仅是二叉树,因为另一个子树也不过是一个列表而已。

myTree = [‘a‘, [‘b‘, [‘d‘,[],[]], [‘e‘,[],[]] ], [‘c‘, [‘f‘,[],[]], []] ]
print(myTree)
print(‘left subtree = ‘, myTree[1])
print(‘root = ‘, myTree[0])
print(‘right subtree = ‘, myTree[2])

现在来为这种树形数据结构定义一些函数以方便使用。注意我们没有定义一个二叉树类,这些函数只是帮助操作一个列表,即使工作对象是一个树。

def BinaryTree(r):

return [r, [], []]

BinaryTree函数简单地创建了一个列表,内中只有一个根节点和两个空列表作为孩子。要增加左子树的话,就要插入一个新列表到根列表的第二个位置上。要小心一点,如果第二个位置上已经保存了数据,需要跟踪这个数据,并把它下压一级,作为新插入列表的左孩子。Listing1是插入左孩子的代码。

Listing 1

def insertLeft(root,newBranch):

t= root.pop(1)

iflen(t)
>1:

root.insert(1,[newBranch,t,[]])

else:

root.insert(1,[newBranch, [], []])

return root

注意,要插入左孩子,先得到原来左孩子位置的列表(可能是空的),当加入新左孩子的时候,把原来的左孩子作为新节点的孩子。这样,我们可以把新节点放在树中任何位置。

插入右孩子的代码也很相似,见下面listing 2.

Listing 2

def insertRight(root,newBranch):

t= root.pop(2)

iflen(t)
>1:

root.insert(2,[newBranch,[],t])

else:

root.insert(2,[newBranch,[],[]])

return root

下面listing 3是获取和赋值的函数,针对根节点和左右子树。

Listing 3

defgetRootVal(root):

return root[0]

defsetRootVal(root,newVal):

root[0]
= newVal

defgetLeftChild(root):

return root[1]

defgetRightChild(root):

return root[2]

下面是完整的函数代码。

def BinaryTree(r):
    return [r, [], []]

def insertLeft(root,newBranch):
    t = root.pop(1)
    if len(t) > 1:
        root.insert(1,[newBranch,t,[]])
    else:
        root.insert(1,[newBranch, [], []])
    return root

def insertRight(root,newBranch):
    t = root.pop(2)
    if len(t) > 1:
        root.insert(2,[newBranch,[],t])
    else:
        root.insert(2,[newBranch,[],[]])
    return root

def getRootVal(root):
    return root[0]

def setRootVal(root,newVal):
    root[0] = newVal

def getLeftChild(root):
    return root[1]

def getRightChild(root):
    return root[2]

r = BinaryTree(3)
insertLeft(r,4)
insertLeft(r,5)
insertRight(r,6)
insertRight(r,7)
l = getLeftChild(r)
print(l)

setRootVal(l,9)
print(r)
insertLeft(l,11)
print(r)
print(getRightChild(getRightChild(r)))

节点与引用方法

第二种表示方法使用节点和引用。在这种情况下我们定义一个类,它的属性值包括根,和左右子树。因为这种方法更加面向对象,以后的章节中,我们都用这种表示方法。

使用节点和引用,我们认为树形结构类似图2所示。

开始时如linsting 4所示,只是一个节点的定义和两个子树的引用。重要的是,左右子树的引用的,也是这个类的实例。例如,如果要插入一个新的左子树,那么创建一个新的树对象并且修改根节点的self.leftChild指向新树

Listing4

classBinaryTree:
    def__init__(self,rootObj):
        self.key= rootObj
        self.leftChild=None
        self.rightChild=None

注意上面代码中,构造函数需要一个对象来保存在根节点。既然列表可以保存任意对象,那么树的根可以是任意对象。在前面的例子中,我们在根节点中保存节点的名字。在图2中那样的树,我们要创建6个BinaryTree的对象。

下面看另外的函数。要在树中增加一个左子树,需要新建一个二叉树对象,并把这个对象赋值给leftChild。下面是实现代码。

Listing5

1
2
3
4
5
6
7

definsertLeft(self,newNode):
    ifself.leftChild==None:
        self.leftChild= BinaryTree(newNode)
    else:
        t = BinaryTree(newNode)
        t.leftChild=self.leftChild
        self.leftChild= t

上面代码中考虑了两种情况。第一种是没有左孩子,只需把节点加到树上。第二种是此节点已经有左孩子,这时要把现存的左孩子下移一级作为插入节点的左孩子。第二种情况是在上面代码中第4行的else语句处理的。

插入右孩子的操作也要对称地考虑,要么没有右孩子,否则把节点插入到根与现存右孩子之间。如下面的listing 6

Listing6

definsertRight(self,newNode):
    ifself.rightChild==None:
        self.rightChild= BinaryTree(newNode)
    else:
        t = BinaryTree(newNode)
        t.rightChild=self.rightChild
        self.rightChild= t

同样的,下面的函数是用来取值和赋值的。

Listing7

defgetRightChild(self):
    returnself.rightChild
 
defgetLeftChild(self):
    returnself.leftChild
 
defsetRootVal(self,obj):
    self.key= obj
 
defgetRootVal(self):
    returnself.key

现在我们完成了创建和操作树所需要的全部程序段,现在用它们来验证一下他们树的结构。我们先创建一个简单的树包含一个根和两个节点,b和c。下面的代码就是创建树,并为键,左孩子和右孩子赋值。注意左孩子和右孩子和根都是同一个类BinaryTree的不同对象,如同前面我们的递归定义一样,这使得我们可以象处理二叉树一样处理它的子树。

class BinaryTree:
    def __init__(self,rootObj):
        self.key = rootObj
        self.leftChild = None
        self.rightChild = None

    def insertLeft(self,newNode):
        if self.leftChild == None:
            self.leftChild = BinaryTree(newNode)
        else:
            t = BinaryTree(newNode)
            t.leftChild = self.leftChild
            self.leftChild = t

    def insertRight(self,newNode):
        if self.rightChild == None:
            self.rightChild = BinaryTree(newNode)
        else:
            t = BinaryTree(newNode)
            t.rightChild = self.rightChild
            self.rightChild = t

    def getRightChild(self):
        return self.rightChild

    def getLeftChild(self):
        return self.leftChild

    def setRootVal(self,obj):
        self.key = obj

    def getRootVal(self):
        return self.key

r = BinaryTree(‘a‘)
print(r.getRootVal())
print(r.getLeftChild())
r.insertLeft(‘b‘)
print(r.getLeftChild())
print(r.getLeftChild().getRootVal())
r.insertRight(‘c‘)
print(r.getRightChild())
print(r.getRightChild().getRootVal())
r.getRightChild().setRootVal(‘hello‘)
print(r.getRightChild().getRootVal())

python数据结构与算法 37 树的实现

时间: 2024-10-06 19:23:59

python数据结构与算法 37 树的实现的相关文章

python数据结构与算法 36 树的基本概念

树 学习目标 理解什么是树及使用方法 学会使用树实现映射 用列表实现树 用类和引用实现树 用递归实现树 用堆实现优先队列 树的例子 前面我们学习过栈和队列这类线性数据结构,并且体验过递归,现在我们学习另一种通用数据结构,叫做树.树在计算机科学中应用广泛,象操作系统.图形学.数据库系统.网络等都要用到树.树和他们在自然界中的表哥--植物树--非常相似,树也有根,有分枝,有叶子.不同之处是,数据结构的树,根在顶上,而叶子在底部. 在开始学习之前,我们来研究几个普通的例子.第一个是生物学上的分级树.图

python数据结构与算法 39 树的遍历

树的遍历 在学习完成树的基本结构以后,我们开始研究一些树的应用模式.访问树的全部节点,一般有三种模式,这些模式的不同之处,仅在于访问节点的顺序不同.我们把这种对节点的访问称为"遍历",这三种遍历模式叫做前序.中序和后序.下面我们对遍历模式作更仔细的定义,同时研究使用这延续模式的例子. 前序遍历 在前序遍历中,先访问根节点,然后用递归方式前序遍历它的左子树,最后递归方式前序遍历右子树. 中序遍历 在中序遍历中,先递归中序遍历左子树,然后访问根节点,最后递归中序遍历右子树. 后序遍历 在后

python数据结构与算法——字典树

1 class TrieTree(): 2 def __init__(self): 3 self.root = {} 4 5 def addNode(self,str): 6 # 树中每个结点(除根节点),包含到该结点的单词数,以及该结点后面出现字母的键 7 nowdict = self.root 8 for i in range(len(str)): 9 if str[i] not in nowdict: # 发现新的组合方式 10 nowdict[str[i]] = {'count':0,'

python数据结构与算法 38 分析树

分析树 树的结构完成以后,该是时候看看它能做点什么实事儿了.这一节里,我们研究一下分析树.分析树能够用于真实世界的结构表示,象语法或数学表达式一类的. 图1 一个简单语句的分析树 图1所示是一个简单语句的层级结构,把语句表示为树结构可以让我们用子树来分析句子的组成部分. 图2 ((7+3)?(5?2))的分析树 我们也可以把数学表达式如((7+3)?(5?2))表示为分析树,如图2.此前我们研究过完全括号表达式,这个表达式表达了什么呢?我们知道乘法的优先级比加减要高,但因为括号的关系,在做乘法之

python数据结构与算法 34 归并排序

归并排序 在提高排序算法性能的方法中,有一类叫做分而治之.我们先研究其中第一种叫做归并排序.归并排序使用递归的方法,不停地把列表一分为二.如果列表是空或只有一个元素,那么就是排好序的(递归基点),如果列表有超过1个的元素,那么切分列表并对两个子列表递归使用归并排序.一旦这两个列表排序完成,称为"归并"的基本操作开始执行.归并是把两个有序列表合并成一个新的有序列表的过程.图10是我们熟悉的列表样例分解过程,图11是归并的过程. 图10  切分过程 图11  归并过程 以下是mergeSo

java数据结构与算法之树基本概念及二叉树(BinaryTree)的设计与实现

[版权申明]未经博主同意,不允许转载!(请尊重原创,博主保留追究权) http://blog.csdn.net/javazejian/article/details/53727333 出自[zejian的博客] 关联文章: java数据结构与算法之顺序表与链表设计与实现分析 java数据结构与算法之双链表设计与实现 java数据结构与算法之改良顺序表与双链表类似ArrayList和LinkedList(带Iterator迭代器与fast-fail机制) java数据结构与算法之栈(Stack)设

Python数据结构与算法--List和Dictionaries

Lists 当实现 list 的数据结构的时候Python 的设计者有很多的选择. 每一个选择都有可能影响着 list 操作执行的快慢. 当然他们也试图优化一些不常见的操作. 但是当权衡的时候,它们还是牺牲了不常用的操作的性能来成全常用功能. 本文地址:http://www.cnblogs.com/archimedes/p/python-datastruct-algorithm-list-dictionary.html,转载请注明源地址. 设计者有很多的选择,使他们实现list的数据结构.这些选

python数据结构与算法 35 快速排序

快速排序 快速排序也使用了分而治之的策略来提高性能,而且不需要额外的内存,但是这么做的代价就是,列表不是对半切分的,因而,性能上就有所下降. 快速排序选择一个数值,一般称为"轴点",虽然有很多选取轴点的方法,我们还是简单地把列表中第一个元素做为轴点了.轴点的作用是帮助把列表分为两个部分.列表完成后,轴点所在的位置叫做"切分点",从这一点上把列表分成两部分供后续调用. 图12所示,54将作为轴点.这个例子我们已经排过多次了,我们知道54在排好序后将处于现在31的位置上

Python数据结构与算法--算法分析

在计算机科学中,算法分析(Analysis of algorithm)是分析执行一个给定算法需要消耗的计算资源数量(例如计算时间,存储器使用等)的过程.算法的效率或复杂度在理论上表示为一个函数.其定义域是输入数据的长度,值域通常是执行步骤数量(时间复杂度)或者存储器位置数量(空间复杂度).算法分析是计算复杂度理论的重要组成部分. 本文地址:http://www.cnblogs.com/archimedes/p/python-datastruct-algorithm-analysis.html,转