1.1 算法
解决问题的办法,是一种独立的存在的解决问题的方法和思想,它不依赖于代码。代码只不过是对算法的一种表达和实现。
1.2 数据结构
存储和组织数据的方式
1.3 时间复杂度
在规模量级上对算法的衡量,表示一个算法随着问题规模不断变化的最主要趋势
基本操作
:只有常数项,时间复杂度为O(1)顺序结构
:时间复杂度按加法
进行计算循环结构、递归结构
:时间复杂度按乘法
进行计算分支结构
:时间复杂度取最大值
最高次项
,忽略系数、低阶及常数项最坏时间复杂度
(提供了一种保证,表明算法在此种程度的基本操作中一定能完成工作。)函数嵌套
,时间复杂度按乘法
计算O(1) 执行次数恒定的计算
1.4 空间复杂度
一个算法在运行过程中临时消耗内存的大小的度量
1.5 算法的复杂度
算法的时间复杂度和空间复杂度的合称
1.6 算法五大特性
①输入: 算法具有0个或多个输入
②输出: 算法至少有1个或多个输出
③有穷性: 算法在有限的步骤之后会自动结束而不会无限循环,并且每一个步骤可以在可接受的时间内完成
④确定性:算法中的每一步都有确定的含义,不会出现二义性
⑤可行性:算法的每一步都是可行的,也就是说每一步都能够执行有限的次数完成
1.7 常见时间复杂度
执行次数函数举例 | 阶 | 非正式术语 |
---|---|---|
12 | O(1) | 常数阶 |
2n+3 | O(n) | 线性阶 |
3n2+2n+1 | O(n2) | 平方阶 |
5log2n+20 | O(logn) | 对数阶 |
6n3+2n2+3n+4 | O(n3) | 立方阶 |
2.1 数据结构
数据对象中数据元素之间的关系-
2.1.1 程序 = 数据结构(问题载体) + 算法
高效的程序需要在数据结构基础上设计和选择算法
2.1.2 元素外置:地址和数据分开存储
4位
8位
2.1.3 数据结构
1 线性结构
1.1 顺序表
1.2 链表
1.3 栈
1.3.1 特点:
① 一种运算受限的线性表
,仅允许在表的一端进行插入和删除运算,这一端被称为栈顶
,相对地,把另一端称为栈底
.
② 处理数据的时候符合先进后出(FILO)
特点
1.3.2 作用:函数里面有可能要使用到局部变量, 不能总是用全局变量. 而局部变量在函数使用完毕之后就销毁了, 那么将局部变量存储在栈中既能不浪费空间又能及时销毁.
1.3.3 代码实现
1 链表实现栈
class Node:
def __init__(self, data, next=None):
self.data = data
self.next = next
class LinkedStack:
def __init__(self):
# 最上面的元素
self.top = None
def push(self, value: int):
newtop = Node(value)
newtop.next = self.top
self.top = newtop
def pop(self):
if self.top:
value = self.top.data
self.top = self.top.next
return value
if __name__ == "__main__":
stack = LinkedStack()
for i in range(9):
stack.push(i)
for i in range(9):
ele = stack.pop()
print(ele)
1.4 队列
特点:
① 一种操作受限的线性表
,只允许在表的头部(front)(队头)进行删除操作,而在表的尾部(rear)(队尾)进行插入操作。
② 处理数据的时候符合先进先出(FIFO)
特点
作用:
在任务处理类的系统中,先把用户发起的任务请求接收过来存到队列中,然后后端开启多个应用程序从队列中取任务进行处理,队列可以起到了缓冲压力
的作用
代码实现
列表队列优化
# 尾部入 头部出
class ArrayQueue:
def __init__(self, capacity):# capacity 容量
# 申请固定长度空间
self.__items = [0]*capacity
self.__capacity = capacity
# 头部指针
self.__head = 0
# 尾部指针(下一个元素添加到这个位置)
self.__tail = 0
def getItmes(self):
return self.__items
def enqueue(self, item):
# 如果存满了,返回False
if self.__head==0 and self.__tail==self.__capacity:
return False
# 没有存满,指针到最后了 需要先把元素往前挪
if self.__tail==self.__capacity:
# 指针到最后了,容量没有满
for i in range(0, self.__tail - self.__head):
self.__items[i] = self.__items[i + self.__head]
# 修改尾部指针
self.__tail = self.__tail - self.__head
# 修改头部指针
self.__head = 0
# 元素添加
self.__items[self.__tail] = item
self.__tail += 1
return True
def dequeue(self):
# 非空
if self.__head != self.__tail:
item = self.__items[self.__head]
# 把头部往后移动1位
self.__head += 1
return item
else:
return None
arrayQ = ArrayQueue(6)
arrayQ.enqueue(10)
arrayQ.enqueue(20)
print(arrayQ.getItmes())
print(arrayQ.dequeue())
print(arrayQ.getItmes())
arrayQ.enqueue(30)
arrayQ.enqueue(40)
arrayQ.enqueue(50)
arrayQ.enqueue(60)
arrayQ.enqueue(70)
print(arrayQ.getItmes())
链表实现队列
# 节点
class Node:
def __init__(self, data, next=None):
# 数据
self.data = data
# 链接域
self.next = next
class LinkedQueue:
def __init__(self):
self.__head = None
self.__tail = None
def enqueue(self, value):
'''入队列'''
new_node = Node(value)
if self.__tail:
# 非空
self.__tail.next = new_node
else:
# 空链表
self.__head = new_node
# 尾部指向新的节点
self.__tail = new_node
def dequeue(self):
if self.__head:
# 头部不为空
# 获取头部元素
value = self.__head.data
# 头部指向原来的下一个节点
self.__head = self.__head.next
if not self.__head:
# 如果头部指向None 链表为空
self.__tail = None
return value
if __name__ == "__main__":
linkQ = LinkedQueue()
linkQ.enqueue(10)
linkQ.enqueue(20)
linkQ.enqueue(30)
print(linkQ.dequeue())
print(linkQ.dequeue())
print(linkQ.dequeue())
双端队列
特点:
2 非线性结构
使一串记录,按照其中的某个或某些关键字的大小,递增或递减的排列起来的操作。
假定在待排序的记录序列中,存在多个具有相同的关键字的记录,若经过排序,这些记录的相对次序保持不变,则称这种排序算法是稳定的, 否则称为不稳定的。
不稳定算法
: 选择排序、快速排序、希尔排序、堆排序
稳定算法
: 冒泡排序、插入排序、归并排序和基数排序
3.3.1 代码实现
1 冒泡排序
特点:
重复地走访过要排序的元素列,依次比较两个相邻的元素,如果顺序(如从大到小、首字母从从Z到A)错误就把他们交换过来。走访元素的工作是重复地进行直到没有相邻元素需要交换,也就是说该元素列已经排序完成
名字由来:因为越小的元素会经由交换慢慢“浮”到数列的顶端(升序或降序排列),就如同碳酸饮料中二氧化碳的气泡最终会上浮到顶端一样,故名“冒泡排序”
复杂度分析:
最差时间复杂度 : O(n^2)
最优时间复杂度 : O(n)
# 遍历一遍发现没有任何元素发生了位置交换,终止排序
算法稳定性 : 稳定算法
原地排序:是
# n-1+n-2+...+1
def bubble_sort(alist):
'''冒泡排序'''
length = len(alist)
count= 0
for i in range(length-1):
flag = False
for j in range(length-1-i):
count+=1
if alist[j] > alist[j+1]:
alist[j], alist[j+1] = alist[j+1], alist[j]
flag = True
if not flag:
break
print(count)
# 4+3+2+1
if __name__ == '__main__':
# l = [5,3,4,7,2]
l = [2,3,4,5,7]
bubble_sort(l)
print(l)
2 选择排序
特点:
第一次从待排序的数据元素中选出最小(或最大)的一个元素,存放在序列的起始位置,然后再从剩余的未排序元素中寻找到最小(大)元素,然后放到已排序的序列的末尾. 以此类推,直到全部待排序的数据元素的个数为零.
复杂度分析:
最差时间复杂度 : O(n^2)
最优时间复杂度 : O(n^2)
算法稳定性 : 不稳定算法
原地排序:是
def select_sort(alist):
# 5
length = len(alist)
for i in range(length-1):
# 设置目标
# targetIndex = i
# 设置最小值索引
minIndex = i
for j in range(i+1,length):
if alist[j] < alist[minIndex]:
minIndex = j
# 如果目标和最小值索引不同 交换
if minIndex!=i:
alist[i], alist[minIndex] = alist[minIndex], alist[i]
if __name__ == '__main__':
alist = [1,3,4,10,0,1000,88]
select_sort(alist)
print(alist)
3 插入排序
特点:
将一个数据插入到已经排好序的有序数据中,从而得到一个新的、个数加一的有序数据,算法适用于少量数据的排序。(每步将一个待排序的记录,按排序要求插入到前面已经排序的数据中适当位置上,直到全部插入完为止)
复杂度分析:
O(n^2)
O(n)
def insert_sort(alist):
length = len(alist)
for i in range(1,length):
# i每一次需要排序的结束索引
# 0 - 1
for j in range(i,0,-1):
if alist[j] < alist[j-1]:
alist[j], alist[j-1] = alist[j-1], alist[j]
else:
break
if __name__ == '__main__':
alist = [1, 100, 99, 20, 5, 1000]
insert_sort(alist)
print(alist)
4 快速排序
特点:
通过一趟排序将要排序的数据分割成独立的两部分,其中一部分的所有数据都比另外一部分的所有数据都要小,然后再按此方法对这两部分数据分别进行快速排序,整个排序过程可以递归进行,以此达到整个数据变成有序序列。
排序流程:
(1) 首先设定一个分界值,通过该分界值将数组分成左右两部分
(2) 将大于或等于分界值的数据集中到数组右边,小于分界值的数据集中到数组的左边,此时,左边部分中各元素都小于或等于分界值,而右边部分中各元素都大于或等于分界值
(3) 然后,左边和右边的数据可以独立排序。对于左侧的数组数据,又可以取一个分界值,将该部分数据分成左右两部分,同样在左边放置较小值,右边放置较大值。右侧的数组数据也做类似处理
(4) 重复上述过程,可以看出,这是一个递归定义。通过递归将左侧部分排好序后,再递归排好右侧部分的顺序,当左、右两个部分各数据排序完成后,整个数组的排序也就完成了
复杂度分析:
最优时间复杂度: O(nlogn)
最差时间复杂度: O(n^2)
算法稳定性: 不稳定
原地排序:是
def quick_sort(alist,start,end):
if start>=end:
return
# 界限值
mid = alist[start]
# 左右游标
left = start
right = end
while left < right:
# 从右边开始找寻小于mid的值 归类到左边
while alist[right] >= mid and left < right:
right -= 1
alist[left] = alist[right]
# 从左边开始找寻大于mid的值 归类到右边
while alist[left] < mid and left < right:
left += 1
alist[right] = alist[left]
# left=right
alist[left] = mid
# 排序左边
quick_sort(alist,start,left-1)
# 排序右边
quick_sort(alist,left+1,end)
if __name__ == '__main__':
alist = [5,9,8,7,6,4,3,2,1]
quick_sort(alist,0,len(alist)-1)
print(alist)
特点
分查找又称折半查找
,它是一种效率较高的查找方法,①必须采用顺序存储结构 ②必须按关键字大小有序排列。
基本思想
数组分为三部分,依次是中值前,中值,中值后,将要查找的值与中值进行比较,若小于中值则在中值前面找,若大于中值则在中值后面找,等于中值时直接返回。
复杂度分析
最差时间复杂度: O(logn)
最优时间复杂度: O(1)
def binary_search(alist, item):
# 数列的长度
n = len(alist)
if n==0:
return False
# 递归的结束条件
# 中间值索引
mid = n // 2
if item == alist[mid]:
return True
elif item < alist[mid]:
# 左边
return binary_search(alist[:mid],item)
elif item > alist[mid]:
# 右边
return binary_search(alist[mid+1:],item)
if __name__ == '__main__':
alist = [1,2,3,4,5,6,7,8,9]
print(binary_search(alist, 5))
def binary_search_achieve(alist, item, start, end):
"""二分查找"""
if start > end:
return -1
# 中间值索引
mid = (start + end) // 2
if item == alist[mid]:
return mid
elif item < alist[mid]:
return binary_search_achieve(alist, item, start, mid - 1)
elif item > alist[mid]:
return binary_search_achieve(alist, item, mid + 1, end)
def binary_search(alist, item):
return binary_search_achieve(alist, item, 0, len(alist) - 1)
# 40亿 32 2^32
if __name__ == '__main__':
alist = [1, 2, 3, 4, 5, 6, 7, 8, 9]
print(binary_search(alist, 8))
print(binary_search(alist, 100))
def binary_search(alist, item):
"""二分查找"""
# 设置起始位置 获取中间值
start = 0
end = len(alist) - 1
# 必须要= 需要比较最后的一个元素是否是需要的数据
while start <= end:
# 获取中间值
# mid = (start+end)//2
# end//2+start//2
mid = start+(end-start)//2
# start+(end-start)//2
if item == alist[mid]:
return mid
elif item < alist[mid]:
end = mid - 1
elif item > alist[mid]:
start = mid + 1
# 没有找到想要找的数字
return -1
# 最好 O(1) 最坏 O(logn)
if __name__ == '__main__':
alist = [1,2,3,4,5,6,7,8,9]
print(binary_search(alist, 1))
print(binary_search(alist, 5))
print(binary_search(alist, 100))
def binary_search_first(alist, item):
"""二分查找 如果找到返回索引 没有找到返回-1"""
# 设置起始位置 获取中间值
start = 0
end = len(alist) - 1
# 必须要= 需要比较最后的一个元素是否是需要的数据
while start <= end:
# 获取中间值
mid = (start + end)//2
if item == alist[mid]:
# 索引为0 不需要继续查找
if mid==0 or alist[mid-1]!=item:
return mid
else:
end = mid - 1
elif item < alist[mid]:
end = mid - 1
elif item > alist[mid]:
start = mid + 1
# 没有找到想要找的数字
return -1
# 最好 O(1) 最坏 O(logn)
if __name__ == '__main__':
alist = [1,2,3,4,5,5,5,6,7,8,9]
# print(binary_search(alist, 1))
# print(binary_search(alist, 1))
# 获取最后一个5
# print(binary_search_last(alist, 5))
print(binary_search_first(alist, 5))
def binary_search(alist, item):
"""二分查找 如果找到返回索引 没有找到返回-1"""
# 设置起始位置 获取中间值
start = 0
end = len(alist) - 1
# 必须要= 需要比较最后的一个元素是否是需要的数据
while start <= end:
# 获取中间值
mid = (start + end)//2
if item == alist[mid]:
# 判断是否是第一个3
if mid==0 or alist[mid-1]!=item:
return mid
else:
end = mid - 1
elif item < alist[mid]:
end = mid - 1
elif item > alist[mid]:
start = mid + 1
# 没有找到想要找的数字
return -1
# 最好 O(1) 最坏 O(logn)
if __name__ == '__main__':
alist = [1,2,3,3,3,4,5]
print(binary_search(alist, 3))
def binary_search(alist, item):
"""二分查找 如果找到返回索引 没有找到返回-1"""
# 设置起始位置 获取中间值
start = 0
maxIndex = len(alist) - 1
end = maxIndex
# 必须要= 需要比较最后的一个元素是否是需要的数据
while start <= end:
# 获取中间值
mid = (start + end)//2
if item == alist[mid]:
if mid == maxIndex or alist[mid+1]!=item:
return mid
else:
start = mid + 1
elif item < alist[mid]:
end = mid - 1
elif item > alist[mid]:
start = mid + 1
# 没有找到想要找的数字
return -1
# 最好 O(1) 最坏 O(logn)
if __name__ == '__main__':
alist = [1,2,3,3,3,4,5]
print(binary_search(alist, 3))
5.1 特点
一种非线性结构
,用来模拟具有树状结构性质的数据集合. 它是由n(n>=1)个有限节点组成一个具有层次关系的集合。
①每个节点有零个或多个子节点
②没有父节点的节点称为根节点
③每一个非根节点有且只有一个父节点
④除了根节点外,每个子节点可以分为多个不相交的子树
名字由来:因为它看起来像一棵倒挂的树,也就是说它是根朝上,而叶朝下的.
拓展:线性结构和非线性结构
线性结构:数据元素之间存在着“一对一”的线性关系的数据结构
特点
①集合中必存在唯一的一个"第一个元素”
②集合中必存在唯一的一个"最后的元素"
③除最后元素之外,其它数据元素均有唯一的"后继"
④除第一元素之外,其它数据元素均有唯一的"前驱”
非线性结构:一个结点元素可能对应多个直接前驱和多个后继的数据结构
5.2 树的术语
节点的度
:一个节点含有的子树的个数称为该节点的度树的度
:一棵树中,最大的节点的度称为树的度叶节点或终端节点
:度为零的节点父亲节点或父节点
:若一个节点含有子节点,则这个节点称为其子节点的父节点孩子节点或子节点
:一个节点含有的子树的根节点称为该节点的子节点兄弟节点
:具有相同父节点的节点互称为兄弟节点节点的层次
:从根开始定义起,根为第1层,根的子节点为第2层,以此类推树的高度或深度
:树中节点的最大层次堂兄弟节点
:父节点在同一层的节点互为堂兄弟节点的祖先
:从根到该节点所经分支上的所有节点子孙
:以某节点为根的子树中任一节点都称为该节点的子孙森林
:由m(m>=0)棵互不相交的树的集合称为森林5.3 树的分类
5.3.1 无序树
树中任意节点的子节点之间没有顺序关系,这种树称为无序树,也称为自由树
5.3.2 有序树
树中任意节点的子节点之间有顺序关系,这种树称为有序树
- 霍夫曼树
(用于信息编码):带权路径最短的二叉树称为哈夫曼树或最优二叉树
- B树
:一种对读写操作进行优化的自平衡的二叉查找树,能够保持数据有序,拥有多于两个的子树
1 二叉树
特点:每个节点最多含有两个子树的树,通常子树被称作“左子树”(left subtree)和“右子树”(right subtree)
分类
完全二叉树
:对于一颗二叉树,假设其深度为d(d>1)。除了第d层外,其它各层的节点数目均已达最大值,且第d层所有节点从左向右连续地紧密排列的二叉树。
满二叉树
:所有叶节点都在最底层的完全二叉树
平衡二叉树
(AVL树):当且仅当任何节点的两棵子树的高度差不大于1的二叉树
排序二叉树
(二叉查找树(英语:Binary Search Tree),也称二叉搜索树、有序二叉树)
基本要求
(1)若左子树不空,则左子树上所有节点的值均小于它的根节点的值
(2)若右子树不空,则右子树上所有节点的值均大于它的根节点的值
(3)左、右子树也分别为二叉排序树
排序二叉树包含空树
存储方式
顺序存储
:将数据结构存储在固定的数组中,虽然在遍历速度上有一定的优势,但因所占空间比较大,是非主流二叉树存储方式。链式存储
:由于对节点的个数无法掌握,常见树的存储表示都转换成二叉树进行处理,子节点个数最多为2应用场景
①xml,html等,那么编写这些东西的解析器的时候,不可避免用到树
②路由协议就是使用了树的算法
③mysql数据库索引
④文件系统的目录结构
⑤所以很多经典的AI算法其实都是树搜索,此外机器学习中的decision tree也是树结构
性质:
性质1: 在二叉树的第i层上至多有2^(i-1)
个结点(i>0)
性质2: 深度为k的二叉树至多有2^k-1
个结点(k>0)
性质3: 对于任意一棵二叉树,如果其叶结点数为N0,而度数为2的结点总数为N2,则N0=N2+1
性质4: 最多有n个结点的完全二叉树的深度必为 log2(n+1)
性质5: 对完全二叉树,若从上至下、从左至右编号,则编号为i 的结点,其左孩子编号必为2i
,其右孩子编号必为2i+1
, 其父节点的编号必为i//2
(i=1 时为根,除外)
代码实现
完全二叉树
import queue
class Node(object):
"""节点类"""
def __init__(self, item):
# 数据类型 10
self.item = item
# Node
self.lchild = None
# Node
self.rchild = None
class BinaryTree(object):
"""二叉树"""
def __init__(self, node=None):
# Node类型 根节点
self.root = node
def add(self, item):
add_node = Node(item)
# 空二叉树
if self.root == None:
self.root = add_node
return
my_queue = queue.Queue()
# 把root放入到队列中
my_queue.put(self.root)
while True:
# 获取元素
node = my_queue.get()
# 左右节点是否为空
if node.lchild == None:
node.lchild = add_node
return
if node.rchild == None:
node.rchild = add_node
return
# 放入左右节点
my_queue.put(node.lchild)
my_queue.put(node.rchild)
def breadh_travel(self):
"""广度优先遍历"""
if not self.root:
return
# 定义队列
my_queue = queue.Queue()
# 放入根节点
my_queue.put(self.root)
while not my_queue.empty():
node = my_queue.get()
# 当前节点数据打印
print(node.item,end='')
# 左右节点添加到队列中
if node.lchild:
my_queue.put(node.lchild)
if node.rchild:
my_queue.put(node.rchild)
def preorder_travel_out(self):
self.__preorder_travel(self.root)
def __preorder_travel(self, root):
'''先序遍历: 给根节点 可以把这个节点按照根左右的方式遍历出来'''
if root:
# 根
print(root.item,end='')# 0
# 左子树
self.__preorder_travel(root.lchild)
# 右子树
self.__preorder_travel(root.rchild)
# 给一个节点 就可以对这个节点以及这个节点一下的节点进行中序遍历
def inorder_travel(self, root):
"""中序遍历 左 根 右"""
if root is not None:
self.inorder_travel(root.lchild)
print(root.item, end="")
self.inorder_travel(root.rchild)
# 给一个节点 就可以对这个节点以及这个节点一下的节点进行后序遍历
def postorder_travel(self, root):
"""后序遍历 根 左 右"""
if root is not None:
self.postorder_travel(root.lchild)
self.postorder_travel(root.rchild)
print(root.item, end="")
if __name__ == '__main__':
tree = BinaryTree()
tree.add("0")
tree.add("1")
tree.add("2")
tree.add("3")
tree.add("4")
tree.add("5")
tree.add("6")
tree.add("7")
tree.add("8")
tree.add("9")
tree.postorder_travel(tree.root)
完全二叉树添加节点
import queue
class Node(object):
"""节点类"""
def __init__(self, item):
self.item = item
self.lchild = None
self.rchild = None
class BinaryTree(object):
"""完全二叉树"""
def __init__(self, node=None):
self.root = node
def add(self, item):
"""添加节点"""
if self.root == None:
self.root = Node(item)
return
# 队列
que = queue.Queue()
# 从尾部添加数据
que.put(self.root)
while True:
# 从头部取出数据
node = que.get()
# 判断左节点是否为空
if node.lchild == None:
node.lchild = Node(item)
return
else:
que.put(node.lchild)
if node.rchild == None:
node.rchild = Node(item)
return
else:
que.put(node.rchild)
def breadh_travel(self):
"""广度优先遍历"""
if self.root == None:
return
# 队列
que = queue.Queue()
# 添加数据
que.put(self.root)
while not que.empty():
# while queue:
# 取出数据
node = que.get()
# 当前节点数据
print(node.item, end="")
# 判断左右子节点是否为空
if node.lchild is not None:
que.put(node.lchild)
if node.rchild is not None:
que.put(node.rchild)
if __name__ == '__main__':
tree = BinaryTree()
tree.add("A")
tree.add("B")
tree.add("C")
tree.add("D")
tree.add("E")
tree.add("F")
tree.add("G")
tree.add("H")
tree.add("I")
tree.breadh_travel()
二叉树深度优先遍历
广度优先可以找到最短路径
深度优先往往可以很快找到搜索路径
class Node(object):
"""节点类"""
def __init__(self, item):
self.item = item
self.lchild = None
self.rchild = None
class BinaryTree(object):
"""完全二叉树"""
def __init__(self, node=None):
self.root = node
def add(self, item):
"""添加节点"""
if self.root == None:
self.root = Node(item)
return
# 队列
queue = []
# 从尾部添加数据
queue.append(self.root)
while True:
# 从头部取出数据
node = queue.pop(0)
# 判断左节点是否为空
if node.lchild == None:
node.lchild = Node(item)
return
else:
queue.append(node.lchild)
if node.rchild == None:
node.rchild = Node(item)
return
else:
queue.append(node.rchild)
def breadh_travel(self):
"""广度优先遍历"""
if self.root == None:
return
# 队列
queue = []
# 添加数据
queue.append(self.root)
while len(queue)>0:
# 取出数据
node = queue.pop(0)
print(node.item, end="")
# 判断左右子节点是否为空
if node.lchild is not None:
queue.append(node.lchild)
if node.rchild is not None:
queue.append(node.rchild)
# 给一个节点 就可以对这个节点以及这个节点一下的节点进行先序遍历
def preorder_travel(self, root):
"""先序遍历 根 左 右"""
if root is not None:
print(root.item, end="")
# 需要对1和1以下的节点先序遍历
self.preorder_travel(root.lchild)
self.preorder_travel(root.rchild)
# 给一个节点 就可以对这个节点以及这个节点一下的节点进行中序遍历
def inorder_travel(self, root):
"""中序遍历 左 根 右"""
if root is not None:
self.inorder_travel(root.lchild)
print(root.item, end="")
self.inorder_travel(root.rchild)
# 给一个节点 就可以对这个节点以及这个节点一下的节点进行后序遍历
def postorder_travel(self, root):
"""后序遍历 根 左 右"""
if root is not None:
self.postorder_travel(root.lchild)
self.postorder_travel(root.rchild)
print(root.item, end="")
if __name__ == '__main__':
tree = BinaryTree()
tree.add("0")
tree.add("1")
tree.add("2")
tree.add("3")
tree.add("4")
tree.add("5")
tree.add("6")
tree.add("7")
tree.add("8")
tree.add("9")
tree.preorder_travel(tree.root)
print()
tree.inorder_travel(tree.root)
print()
tree.postorder_travel(tree.root)
根据遍历结果反推二叉树
知道中序遍历
和 先序遍历 或者 后序遍历
就可以推出二叉树的结构