一、树
树是一种抽象的数据类型或是实作这种抽象数据类型的数据结构,用来模拟具有树状结构性质的数据集合,它是由n(n>=1)个有限节点组成一个具有层次关系的集合。
它具有以下的特点:
- 每个节点有零个或多个子节点
- 没有父节点的节点称为根节点
- 每一个非根节点有且只有一个父节点
- 除了根节点外,每个子节点可以分为多个不相交的子树
树的术语:
- 节点的度:一个节点含有的子树的个数称为该节点的度
- 树的度:一棵树中,最大的节点的度称为树的度
- 叶节点或终端节点:度为零的节点
- 父亲节点或者父亲点:若一个节点含有子节点,则这个节点称为其子节点的父亲点
- 孩子节点或子节点:一个节点含有的子树的根节点称为该节点的子节点
- 兄弟节点:具有相同父节点的节点互称为兄弟节点
- 节点的层次:从根开始定义起,根为第一层,根的字节点为第二层,以此类推
- 树的高度或深度:树中节点的最大层次
- 堂兄弟节点:父节点在同一层的节点互为堂兄弟
- 节点的祖先:从根到该节点所经分支上的所有的节点
- 子孙:以某节点为根的子树中任一节点都称为该节点的子孙
- 森林:由m(m>=0)棵互不相交的树的集合称为森林
树的种类:
无序树:树中任意节点的子节点之间没有顺序关系,这种树称为无序树,也称为自由树
有序树:树中任意节点的子节点之间有顺序关系,这种树称为无序树;
二叉树:每个节点最多含有两个子树的树称为二叉树
完全二叉树:对于一颗二叉树,假设其深度为d。除了第d层外,其他层的节点数目均已达最大值,且第d层所有节点从左向右连续的紧密排列,这样的二叉树被称为完全二叉树,其中满二叉树的定义是所有叶节点都在最底层的完全二叉树
平衡二叉树:当且仅当任何节点的两颗子树的高度差不大于1的二叉树
排序二叉树:也称二叉搜索树、有序二叉树
常见的一些树的应用场景:
- xml、html等,那么编写这些东西的解析器时,不可避免用到树
- 路由协议就是使用了树的算法
- mysql数据库索引
- 文件系统的目录结构
- 所以很多经典的AI算法其实都是树搜索,此外机器学习中的desicion tree也是树结构
二、二叉树
二叉树的性质(特性):
- 在二叉树的第i层上至多有2^(i-1)个节点(i>0)
- 深度为k的二叉树至多有2^k-1个节点
- 对于任意一棵二叉树,如果其叶节点数为N0,而度数为2的节点总数为N2,则N0=N2+1
- 具有n个节点的完全二叉树的深度必为log2(n+1)
- 对完全二叉树,若从上至下、从左至右编号,则编号为i的节点,其左孩子编号必为2i,其右孩子编号必为2i+1,其双亲的编号必为i/2(i=1时为根,除外)
【示例】二叉树节点定义及广度优先遍历
#定义二叉树的节点
class Node:
def __init__(self,elem):
self.elem=elem
self.lchild=None
self.rchild=None
class Tree:
def __init__(self):
self.root=None
#添加节点
def add(self,elem):
node=Node(elem)
if self.root==None:
self.root=node
else:
queue=[]
queue.append(self.root)
while queue:
curNode = queue.pop(0)
if curNode.lchild == None:
curNode.lchild = node
return
else:
queue.append(curNode.lchild)
if curNode.rchild == None:
curNode.rchild = node
return
else:
queue.append(curNode.rchild)
#广度优先遍历
def travel(self):
queue=[]
#判断根节点是否存在
if self.root is None:
return
else:
queue.append(self.root)
while queue:
curNode=queue.pop(0)
print(curNode.elem)
if curNode.lchild is not None:
queue.append(curNode.lchild)
if curNode.rchild is not None:
queue.append(curNode.rchild)
if __name__=='__main__':
tree=Tree()
tree.add(1)
tree.add(2)
tree.add(3)
tree.add(4)
tree.add(5)
print('广度优先遍历')
tree.travel()
三、高阶函数
既然变量可以指向函数,函数的参数能接收变量,那么一个函数就可以接收另一个函数作为参数,这种函数就称为高阶函数.
map
map()函数接收两个参数,一个是函数,一个是序列,map将传入的函数依次作用到序列的每个元素,并把结果作为新的list返回
a=[1,2,3,4,5,6,7,8,9]
def f(x):
return x*x
result_list=[]
for i in a:
result_list.append(f(i))
print(result_list)
#使用map实现
it=map(f,a) #返回的it是一个可迭代的对象
#判断是否是可迭代的对象
from collections import Iterator
print('判断是否是可迭代的:',isinstance(it,Iterator))
print(list(it))
【示例】
'''map将数字转换为字符串'''
a=10
#将10转换为字符串
s=str(a)
print(type(s))
a=[1,2,3,4,5,6,7,8,9] #将列表中每个元素转换为字符串
L=map(str,a)
print(list(L))
'''map传递两个列表'''
a=[1,2,3,4]
b=[10,20,30]
def f(x,y):
return x+y
L=map(f,a,b)
print(list(L))
reduce:把一个函数作用在一个序列[x1,x2,x3....]上,这个函数必须接收两个参数,reduce把结果继续和序列的下一个元素作累积计算,其效果为:
reduce(f,[x1,x2,x3,x4])=f(f(fx1,x2),x3),x4)
比如说对一个序列求和,就可以用reduce实现,如下:
from functools import reduce
#计算一个序列的求和
a=[1,2,3,4,5,6,7,8,9,10]
sum=0
for i in a:
sum+=i
print('累加和:',sum)
def sumTest(x,y):
return x+y
sum=reduce(sumTest,a)
print('reduce计算列表求和:',sum)
filter:Python内建的filter()函数用于过滤序列,和map()类似,filter()也接收一个函数和一个序列。和map()不同的是,filter()把传入的函数依次作用于每个元素,然后根据返回值是True还是False决定保留还是丢弃该元素
【示例】filter序列中的空字符串删掉
#在一个列表中,删掉偶数,保留奇数
def is_odd(n):
if n%2==0:
return False
else:
return True
L=filter(is_odd,[1,2,3,4,5,6,7,8,9,10])
print(list(L))
#一个序列中的空字符串删掉
a=['A','','B',None,'C',' ']
def not_empty(s):
return s and s.strip()
L=filter(not_empty,a)
print(list(L))
【示例】sorted的使用
#对数值进行排序
sort_list=sorted([-2,-45,23,543,14])
print('默认升序排序:',sort_list)
#逆序排序,给sorted添加reverse参数
sort_list=sorted([-2,-45,23,543,14])
print('逆序:',sort_list)
#对字符串ASCII A:65 a:97
sort_list=sorted(['abc','ad','ABC','D','d','C'])
print('字符串排序',sort_list)
sort_list=sorted(['abc','ad','ABC','D','d','C'],reverse=True)
print('字符串逆序排序',sort_list)
#sorted是高阶函数,他还可以接收一个key函数来实现自定义的排序
#对数值列表,按绝对值的大小排序
sort_list=sorted([-2,-45,23,543,14],key=abs)
print('默认升序排序',sort_list)
#对字符串列表,忽略大小写
sort_list=sorted(['abc','ad','ABC','D','d','C'],reverse=True,key=str.lower)
print('字符串逆序排序:',sort_list)