目录
1 算法的衡量标准
1.1 算法
解决问题的办法,是一种独立的存在的解决问题的方法和思想,它不依赖于代码。代码只不过是对算法的一种表达和实现。
1.2 数据结构
存储和组织数据的方式
1.3 时间复杂度
在规模量级上对算法的衡量,表示一个算法随着问题规模不断变化的最主要趋势
- 计算规则(6条)
基本操作
:只有常数项,时间复杂度为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) | 立方阶 |
-
时间复杂度曲线
- 所消耗的时间从小到大:
O(1) < O(logn) < O(n) < O(nlogn) < O(n2) < O(n3)
- 时间复杂度越低,效率越高
- 所消耗的时间从小到大:
2 数据结构
2.1 数据结构
数据对象中数据元素之间的关系-
- 内置数据结构:list、元组、字典等
- 扩展数据结构:栈、队列等
2.1.1 程序 = 数据结构(问题载体) + 算法
高效的程序需要在数据结构基础上设计和选择算法
2.1.2 元素外置:地址和数据分开存储
- 地址存储占用的内存空间
- 32位操作系统:
4位
- 64位操作系统:
8位
- 32位操作系统:
2.1.3 数据结构
1 线性结构
1.1 顺序表
- 一体式顺序表
- 分离式顺序表
1.2 链表
- 单链表
- 元素域: 存放具体数据
- 链接域: 存放下一个节点的位置
1.3 栈
![在这里插入图片描述](https://i-blog.csdnimg.cn/blog_migrate/f82dfcc5786ade873787146ca58b6a21.png)
-
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 队列
![在这里插入图片描述](https://img-blog.csdnimg.cn/20210414114428102.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzM2NzIyODg3,size_16,color_FFFFFF,t_70#pic_center%20=500x)
-
特点:
① 一种操作受限的
线性表
,只允许在表的头部(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())
-
双端队列
-
![在这里插入图片描述](https://img-blog.csdnimg.cn/20210414114447732.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzM2NzIyODg3,size_16,color_FFFFFF,t_70#pic_center%20=500x)
特点:
- 一种具有队列和栈的性质的数据结构
- 双端队列中的元素可以从两端弹出,其限定插入和删除操作在表的两端进行
- 双端队列可以在队列任意一端入队和出队
2 非线性结构
- 树
3 排序算法
3.1 排序
使一串记录,按照其中的某个或某些关键字的大小,递增或递减的排列起来的操作。
3.2 算法稳定性
假定在待排序的记录序列中,存在多个具有相同的关键字的记录,若经过排序,这些记录的相对次序保持不变,则称这种排序算法是稳定的, 否则称为不稳定的。
3.3 排序算法
-
不稳定算法
: 选择排序、快速排序、希尔排序、堆排序 -
稳定算法
: 冒泡排序、插入排序、归并排序和基数排序
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)
4 二分查找
4.1 二分查找
-
特点
分查找又称
折半查找
,它是一种效率较高的查找方法,①必须采用顺序存储结构 ②必须按关键字大小有序排列。 -
基本思想
数组分为三部分,依次是中值前,中值,中值后,将要查找的值与中值进行比较,若小于中值则在中值前面找,若大于中值则在中值后面找,等于中值时直接返回。
-
复杂度分析
-
最差时间复杂度:
O(logn)
-
最优时间复杂度:
O(1)
-
4.2 代码实现
4.2.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))
4.2.2 递归优化版本
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))
4.2.3 非递归版本
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))
4.2.4 二分查找-位置
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))
4.2.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))
4.2.6 最后一个位置
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 非线性数据结构-树
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()
-
二叉树深度优先遍历
-
![在这里插入图片描述](https://i-blog.csdnimg.cn/blog_migrate/41bdce681e11649c7c7c8ced685d36bd.png)
-
广度优先可以找到最短路径
-
深度优先往往可以很快找到搜索路径
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)
-
根据遍历结果反推二叉树
-
知道
中序遍历
和先序遍历 或者 后序遍历
就可以推出二叉树的结构