【Python-AI篇】数据结构和算法

embedded/2024/10/22 7:05:13/

1. 算法概念

1.1 什么是数据结构

  1. 存储,组织数据的方式

1.2 什么是算法

  1. 实现业务目的的各种方法和思路
  2. 算法是独立的存在,只是思想,不依附于代码和程序,可以使用不同语言实现(java,python,c)

1.2.1 算法的五大特性

  1. 输入: 算法具有0个或多个输入
  2. 输出: 算法至少有1个或多个输出
  3. 有穷性: 算法在有限的步骤之后会自动结束循环而不会无限循环,并且每一个步骤可以在可接受的时间内完成
  4. 确定性: 算法中每一个步骤都有确定的含义,不会出现二义性
  5. 可行性: 算法的每一步都是可行的,也就是说每一步都能够执行有限的次数完成

穷举法: 将问题的答案一一列举,根据判断条件判断答案是否合适

python"># 如果a+b+c=1000且a^2+b^2=c^2(a,b,c为自然数),求出a, b, c所有可能的组合
import timestart_time = time.time()
for a in range(0, 1001):for b in range(0, 1001):for c in range(0, 1001):if a**2 + b**2 == c**2 and a+b+c == 1000:print("a={0}, b={1}, c={2}".format(a, b, c))end_time = time.time()
all_time = end_time-start_time
print(all_time)

执行结果:
在这里插入图片描述
穷举法计算完成需要261秒

方法二:

python"># 如果a+b+c=1000且a^2+b^2=c^2(a,b,c为自然数),求出a, b, c所有可能的组合
# 列举a,b,c所有可能的数值
import timestart_time = time.time()
for a in range(0, 1001):for b in range(0, 1001):c = 1000-a-bif a**2 + b**2 == c**2:print("a={0}, b={1}, c={2}".format(a, b, c))end_time = time.time()
all_time = end_time-start_time
print(all_time)

执行结果:
在这里插入图片描述
方法二计算完成仅需要0.3秒

1.3 时间复杂度

1.3.1 穷举法

  1. 循环次数
    三次循环分别为:1001次 1001次 1001次
  2. 每次循环的步骤
    1000 = a+b+c 3次
    a**2 + b**2 = c**2 5次
    print打印 2次
    代码总执行次数: T = 1001 * 1001 * 1001 * (5+3+2) 次
    总结成时间复杂度表达式: 当问题规模为n时: T(n) = 10 * n³
  3. 时间复杂度表示算法随着问题规模不断变化的最主要趋势, 衡量算法的量级
  4. 大O计法:将次要关系全部省略掉,由最高次项表示,最终生成一个表达式:T(n) = O(n³)

1.3.2 方法二

  1. T(n) = O(n2)

1.3.3 时间复杂度计算规则

  1. 基本操作: 时间复杂度为O(1)
  2. 顺序结构: 时间复杂度按加法计算
  3. 循环结构: 时间复杂度按乘法计算
  4. 分支结构: 时间复杂度取最大值
  5. 判断算法效率: 只需要关注操作数量的最高次项,其他次要项和常数项可忽略
  6. 在没有特殊说明时,我们所分析的算法时间复杂度都是指最坏时间复杂度

1.3.4 常见时间复杂度

举例时间复杂度非正式术语
12
O(1)
常数阶
2n+3
O(n)
线性阶
3n 2+2n+1
O(n 2)
平方阶
5log 2n+20
O(logn)
对数阶
6n 3+2n 2+3n+4
O(n 3)
立方阶

消耗时间从小到大: O(1) < O(logn) < O(n) < O(nlogn) < O(n2) < O(n3) < O(n!)

1.4 空间复杂度

  1. 空间复杂度(S(n))定义该算法所消耗的存储空间,临时占用存储空间大小的度量

2. 数据结构

  1. 概念: 存储、组织数据的方式,相互之间存在一种或多种特定关系的数据元素的集合,往往同高效的算法有关
  2. 作用: 相同的数据用不同的方式也就是不同的数据结构存储,那么带来的运行或存储效率也是不同的

示例
存储方式一:列表

python">people1 =[{"name":"aaa", "age":18}, {"name":"bbb","age":19}]
# 查找bbb
for peo in people1:if peo["name"] == "bbb":print("over")

时间复杂度为:O(n)

存储方式二:字典

python">people1 ={{"name":"aaa", "age":18}, {"name":"bbb","age":19}}
# 查找bbb
if "bbb" in people1:print("over")

时间复杂度为:O(1)

  1. 算法和数据结构的区别:
    1. 数据结构只是静态描述了数据元素的关系,高效的程序需要再在数据结构的基础上设计和选择算法
    2. 算法是为了解决实际问题而设计的,数据结构是算法需处理问题的载体

2.1 数据结构的分类

  1. 线性结构:表中各个节点具有线性关系(栈,队列)
  2. 非线性结构: 表中各个节点具有多个对应关系(树,图)

3. 顺序表

  1. 顺序表: 将元素顺序的放在一块连续的存储区里,元素间的关系由他们的存储顺序自然表示
  2. 链表: 将元素存放在通过链接构造起起来的一系列存储块中,存储区是非连续的

3.1 顺序表分类

  1. 一体式结构
  2. 分离式结构
  3. 无论是一体式结构还是分离式结构,在获取数据的时候直接通过下标偏移就可以找到数据所在空间的地址,而无需遍历后才可以获取地址,所以顺序表在获取地址操作的时间复杂度为:O(1)
  4. 顺序表完整信息:
    1. 数据区
    2. 信息区:即元素存储容量和当前表中已有元素的个数
  5. 顺序表的扩充
    1. 每次扩充增加固定数目和存储位置,如每次扩充10个位置,可称为线性增长
      特点:节省空间,但扩充操作频繁,操作次数多
    2. 每次扩充容量加倍,如每次扩充增加一倍的存储空间
      特点:减少了扩充的次数,但可能会浪费空间资源,以空间换取时间,推荐的方式
  6. 元素存储区替换:顺序表存储连续空间,如果下方扩充空间被占用,只能整体搬迁

3.2 顺序表增加删除元素

3.2.1 增加元素

  1. 尾端加入元素,时间复杂度为O(1)
  2. 非保序的加入元素(不常见),时间复杂度为O(1)
  3. 保序的元素加入,时间复杂度为O(n)

3.2.2 删除元素

  1. 尾端删除元素,时间复杂度为O(1)
  2. 非保序的删除元素(不常见),时间复杂度为O(1)
  3. 保序的元素删除,时间复杂度为O(n)

4. 链表

4.1 链表结构

  1. 单链表: 链表的一种形式,每个节点包含两个域,一个元素域和一个链接域,这个链接指向链表中下一个节点,而最后一个节点的链接域指向一个空值
  2. 表元素域elem:用来存放具体的数据
  3. 链接域next:用来存放下一个节点位置
  4. 变量head:指向链表的头结点的位置,从head出发能找到表中任意节点
python"># item 存放元素
# next 标识下一个节点
class SingleNode:def __init__(self, item):self.item = item# 标识下一个节点self.next = None# 单链表实现
# head 首结点
class SingleLink:def __init__(self, node=None):# 首结点self.head = node
# 结点
node1 = SingleNode(10)
print(node1.item)
print(node1.next)
# 链表
link1 = SingleLink()
print(link1.head)
link2 = SingleLink(node1)
print(link2.head)
print(link2.head.item)

在这里插入图片描述

4.2 链表的代码实现

---- 判空,长度,遍历,增加,删除

python"># 链表节点实现
# item 存放元素
# next 标识下一个节点
class SingleNode:def __init__(self, item):self.item = item# 标识下一个节点self.next = None# 单链表实现
# head 首结点
class SingleLink:def __init__(self, node=None):# 首结点self.head = node# 判断链表是否为空def is_empty(self):if self.head is None:return Trueelif self.head is not None:return False# 获取链表的长度def length(self):cur = self.headcount = 0while cur is not None:count += 1cur = cur.nextreturn count# 遍历链表def traverse(self):cur = self.headwhile cur is not None:print(cur.item)cur = cur.next# 头部增加结点def add(self, item):node = SingleNode(item)node.next = self.headself.head = node# 尾部增加结点def append(self, item):node = SingleNode(item)# 判断是否为空链表if self.is_empty():self.head = nodeelse:cur = self.head# 找到尾结点while cur.next is not None:cur = cur.nextcur.next = node# 指定位置增加结点def insert(self, pos, item):if pos <= 0:#头部增加新结点self.add(item)elif pos >= self.length():self.append(item)else:# 游标cur = self.head# 计数count = 0# 新结点node = SingleNode(item)# 找到插入位置的前一个结点while count < pos -1:cur = cur.nextcount += 1# 完成插入新结点node.next = cur.nextcur.next = node# 删除结点def remove(self, item):cur = self.headpre = Nonewhile cur is not None:# 找到了删除元素if cur.item == item:# 要删除的元素在头部if cur == self.head:self.head = cur.nextelse:# 要删除元素不在头部pre.next = cur.nextreturnelse:# 没有找到要删除元素pre = curcur = cur.nextdef search(self, item):# 游标cur = self.headwhile cur is not None:if cur.item == item:return Truecur = cur.nextreturn Falseif __name__ == '__main__':# 结点node1 = SingleNode(10)print(node1.item)print(node1.next)# 链表hylink1 = SingleLink()print(link1.head)link2 = SingleLink(node1)print(link2.head)print(link2.head.item)# 判空print(link1.is_empty())print(link2.is_empty())# 长度print(link1.length())print(link2.length())# 遍历print(link2.traverse())# 头部增加结点link2.add(9)link2.traverse()# 尾部增加结点link2.append(11)link2.traverse()# 指定位置增加结点link2.insert(2, 7)link2.traverse()link2.insert(-1, 7)link2.traverse()link2.insert(9, 7)link2.traverse()# 删除结点link2.remove(9)link2.traverse()# 查找结点是否存在print(link2.search(7))print(link2.search(9))

5. 栈

运算受限的线性表,只允许在表的一端进行插入和删除,这一端被称为栈顶,另一端被称为栈底
符合先进后出的特点

5.1 栈的作用

  1. 计算机系统里面CPU结构的一部分
  2. 局部变量在函数使用完毕后销毁,存放进栈即不浪费空间,又能及时销毁
python"># 使用列表封装改造为栈,尾部操作效率更高
class Stack:"""栈"""def __init__(self):self.__items = []def push(self, item):"""进栈"""self.__items.append(item)def pop(self):"""出栈"""self.__items.pop()def travel(self):"""遍历"""a = []for item in self.__items:a.append(item)return aif __name__ == '__main__':my_stack = Stack()my_stack.push(10)my_stack.push(20)my_stack.push(30)print('-------进栈---------')a = my_stack.travel()print(a)my_stack.pop()print('-------出栈后---------')a = my_stack.travel()print(a)

在这里插入图片描述

6. 队列

特殊的线性表,只允许在头部进行删除操作,在尾部进行添加操作,插入操作端称为队尾,删除操作称为队头
符合先进先出的特点

6.1 队列的作用

  1. 任务处理类系统:先把用户发送过来的请求存储在队列中,然后开启多个应用程序从队列中取任务进行处理,起到缓冲压力的作用

6.2 队列代码实现

python"># 使用列表封装改造成队列
# 创建一个空队列
class Queue:def __init__(self):self.items = []# 队列尾部添加元素def enqueue(self, item):self.items.append(item)# 队列头部删除数据def dequeue(self):self.items.pop(0)# 判断队列为空def is_empty(self):return self.items == []# 返回队列大小def size(self):return len(self.items)def traverse(self):item_list = []for i in self.items:item_list.append(i)return item_listif __name__ == '__main__':print('--------进队列--------')q = Queue()q.enqueue('a')q.enqueue('b')q.enqueue('c')item_list = q.traverse()print(item_list)print('--------出队列--------')q.dequeue()item_list = q.traverse()print(item_list)is_empty = q.is_empty()print('--------判断队列为空--------')print(is_empty)print('--------返回队列大小--------')print(q.size())

在这里插入图片描述

6.3 双端队列

具有队列和栈的数据结构,元素可以从两端任意插入弹出,其限定插入弹出操作必须在两端进行
可以在队列任意一端入队和出队

6.4 双端队列代码实现

python">class Dequeue:"""双端队列"""def __init__(self):self.items = []def is_empty(self):"""是否为空判断"""return self.items == []def size(self):"""返回队列大小"""return len(self.items)def add(self, item):"""头部增加数据"""self.items.insert(0, item)def add_last(self, item):"""尾部添加数据"""self.items.append(item)def remove(self):"""头部删除数据"""self.items.pop(0)def remove_last(self):"""尾部删除数据"""self.items.pop()def traverse(self):item_list = []for item in self.items:item_list.append(item)return item_listif __name__ == '__main__':dequeue = Dequeue()print(dequeue.is_empty())print(dequeue.size())print('--------头部添加数据--------')dequeue.add(1)dequeue.add(2)item_list = dequeue.traverse()print(item_list)print('--------尾部添加数据--------')dequeue.add_last(3)dequeue.add_last(4)dequeue.add_last(5)item_list = dequeue.traverse()print(item_list)print('--------头部删除数据--------')dequeue.remove()item_list = dequeue.traverse()print(item_list)print('--------尾部删除数据--------')dequeue.remove_last()item_list = dequeue.traverse()print(item_list)

在这里插入图片描述

7. 算法

排序算法稳定性:在待排序的序列中,存在多个关键字记录,若经过排序,这些记录的相对次序保持不变,则称这种排序算法是稳定的,否则是不稳定的

  1. 不稳定的排序算法:选择排序,快速排序,希尔排序,堆排序
  2. 稳定的排序算法:冒泡排序,插入排序,归并排序,基数排序

7.1 冒泡排序

时间复杂度:最差:O(n2),最优O(n),算法稳定性:稳定

python">def bubble_sort(alist):"""冒泡排序"""# 控制比较轮数for j in range(0, len(alist)-1):# 计数count = 0# 控制每一轮的比较次数for i in range(0, len(alist)-j-1):# 比较相邻的两个数字,不符合要求交换位置if alist[i] > alist[i+1]:alist[i], alist[i+1] = alist[i+1], alist[i]count += 1# 如果遍历一遍发现没有数字交换,退出循环,证明数列是有序的if count == 0:breakif __name__ == '__main__':alist = [5, 4, 3, 2, 1]bubble_sort(alist)print(alist)

在这里插入图片描述

7.2 选择排序

工作原理:第一次从待排序的数据中选出最小或最大的元素,存放在序列的起始位置,然后再从剩余的未排序的元素中找出最小或最大的元素,然后放到已排序的序列的末尾,以此类推,直到全部待排序的数据元素个数为0
时间复杂度:最差:O(n2),最优O(n2),算法稳定性:不稳定

python">def select_sort(alist):"""选择排序"""# 列表的长度n = len(alist)# 控制比较轮数for j in range(0, n-1):# 假定最小值下标min_index = j# 控制比较次数for i in range(j+1, n):# 进行比较最小值if alist[i] < alist[min_index]:min_index = i# 如果假定的最小值发生了变化,那么我们就进行交换if min_index != j:alist[min_index], alist[j] = alist[j], alist[min_index]if __name__ == '__main__':alist = [5, 4, 3, 2, 1]select_sort(alist)print(alist)

7.3 快速排序

基本思路:通过一趟排序将要排序的数据独立分割成两个部分,其中一部分的所有数据都比另一部分所有数据都要小,然后以此方法对这两部分的数据分别进行快速排序,整个排序过程可以递归执行,以此达到整个数据变成有序序列
排序流程如下:

  1. 首先设定一个分界值,通过分界值将数组分为左右两个部分
  2. 将大于或等于分界值的数据集中分布在右边,小于分界值的数据集中分布在左边,此时左边部分中各元素都小于分界值,右边部分各元素都大于等于分界值
  3. 左边和右边的数据可以独立排序,对于左侧数据,又可以取一个分界值,将该部分分为左右两部分,同样在左边放置最小值,右边放置最大值,右侧数据也做类似处理
  4. 重复上述步骤,可以看出这是一个递归定义,通过递归将左侧排好序后,再递归排好右侧部分顺序,当左右两个部分各数据排序完成后,整个数组排序也就完成了

时间复杂度:最差:O(n2),最优O(nlogn),算法稳定性:不稳定

python">def quick_sort(alist, start, end):"""快速排序"""# 递归结束条件if start >= end:return# 界限值mid = alist[start]# 左右游标left = startright = endwhile left < right:while alist[right] >= mid and left < right:right = right - 1# 从右边开始找寻小于mid的值,归类到左边alist[left] = alist[right]# 从左边开始找寻小于mid的值,归类到右边while alist[left] <= mid and left < right:left = left + 1alist[right] = alist[left]# 循环一旦结束 证明找到了mid的值alist[left] = mid# 递归操作quick_sort(alist, start, left - 1)quick_sort(alist, right + 1, end)if __name__ == '__main__':alist = [10, 9, 8, 7, 7, 6, 5, 9, 3, 2, 1]quick_sort(alist, 0, len(alist) - 1)print(alist)

在这里插入图片描述

7.4 插入排序

直接将一个数据插入到已经排好序的有序的数据中,从而得到一个新的,个数加一的有序数据,算法适用于少量数据排序
组成:

  1. 有序的数字(默认数组的第一个数字为有序的第一部分)
  2. 部分无序的数字(除了第一个数字之外的数字可以认为是无序的第二部分)

时间复杂度:最差:O(n2),最优O(n),算法稳定性:稳定

python">def insert_sort(alist):"""插入排序"""n = len(alist)# 控制轮数for j in range(1, n):# 找到合适的位置存放无序数据for i in range(j, 0, -1):if alist[i - 1] > alist[i]:alist[i], alist[i - 1] = alist[i - 1], alist[i]else:breakif __name__ == '__main__':alist = [10, 8, 9, 7, 5, 4, 2, 3, 1]insert_sort(alist)print(alist)

在这里插入图片描述

7.5 二分查找

折半查找,效率较高的查找方法
原理:

  1. 将数值分为三部分,中值前,中值和中值后
  2. 将要查找的值依次与中值进行比较,若小于中值则在中值前面找,若大于中值则在中值后面找等于中值则返回
  3. 必须采用顺序表存储结构,必须按关键字大小有序排列

时间复杂度:最差:O(logn),最优O(1),算法稳定性:稳定

7.5.1 递归版本

python">def binary_search(alist, item):"""二分查找"""# 数列长度n = len(alist)# 递归结束条件if n == 0:return False# 中间值mid = n // 2if alist[mid] == item:return Trueelif alist[mid] > item:return binary_search(alist[:mid], item)elif alist[mid] < item:return binary_search(alist[mid + 1:], item)if __name__ == '__main__':my_list = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]print(binary_search(my_list, 10))print(binary_search(my_list, 11))

在这里插入图片描述

7.5.2 非递归版本

python">def binary_search(alist, item):"""二分查找"""# 数列长度n = len(alist)# 设置起始位置,获取中间值start = 0end = n - 1while start <= end:# 中间值mid = start + end // 2if alist[mid] == item:return Trueelif alist[mid] < item:start = mid + 1elif alist[mid] > item:end = mid - 1# 没有找到想要找的数字return Falseif __name__ == '__main__':alist = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]print(binary_search(alist, 10))print(binary_search(alist, 11))

在这里插入图片描述

8. 树

具有模拟树状结构性质的集合,它是由n(n>=1)个有限结点组成一个具有层次关系的集合,把他叫做树

  1. 每个节点有零个或多个子节点
  2. 没有父节点的节点叫做根节点
  3. 每一个非根节点只有一个父节点
  4. 除了根节点以外,每个子节点可以分为多个不相交的树

8.1 树的相关术语:

  1. 节点的度: 一个节点含有子节点的个数
  2. 树的度: 一棵树中,最大节点的度称为树的度
  3. 叶节点或终端节点: 度为0的节点
  4. 父节点: 若一个节点含有子节点,则这个节点称为其子节点的父节点
  5. 子节点: 一个节点含有的子树的根节点称为该节点的子节点
  6. 兄弟节点: 具有相同父节点的节点互称兄弟节点
  7. 节点层次: 从根开始定义,根为第一层,根的子节点为第二层,以此类推
  8. 树的高度和深度: 树种节点的最大层次
  9. 堂兄弟节点: 父节点的同一层节点互称为堂兄弟节点
  10. 节点祖先: 从根到该节点所经历分支上的所有节点
  11. 子孙: 以某节点为根的子树中任意节点
  12. 森林: 由m(m>=0)棵互不相交的树的集合

8.2 树的种类和存储

  1. 无序树: 树中任意节点的子节点之间没有顺序关系,也称为自由树
  2. 有序树: 树中任意节点的子节点之间有顺序关系
    • 霍夫曼树(用于信息编码): 带权路径最短的二叉树称为霍夫曼树或最优二叉树
    • B树: 一种对读写操作进行优化的自平衡的二叉查找树,能够保持数据有序,拥有多于两个的子树
  3. 二叉树: 每个节点最多含有两个子树
    • 完全二叉树: 对于一颗二叉树,假设其深度为d(d>1),除了第d层之外,其他各层的节点数目均已达到最大值,且d层所有节点从左到右连续的紧密排列,这样的二叉树被称为完全二叉树,其中满二叉树的定义是所有叶节点都在最底层的二叉树
    • 平衡二叉树(AVL树): 当且仅当任何节点的两颗子树的高度差不大于1的二叉树
    • 排序二叉树(二叉搜索树、有序二叉树): 要求:①若左子树不空,则左子树上所有节点的值均小于它的根节点的值;②若右子树不空,则右子树上所有节点的值均大于根节点的值;③左右子树分别也为排序二叉树;④排序二叉树包含空树;

8.3 二叉树的存储

  1. 顺序存储: 将数据结构存储在固定的数组中,虽然遍历上有优势,但是所占空间大,是非主流的二叉树存储方式
  2. 链式存储: 由于对节点的个数无法掌握,常见的树的存储表示都转换成二叉树进行处理,子节点最多个数为2,主流二叉树存储方式(指针域个数不定)

8.4 树的应用场景

  1. xml、html等,编写这些东西的解析器的时候,不可避免的需要用到树
  2. 路由协议就是使用了树的算法
  3. mysql数据索引
  4. 文件系统的目录结构
  5. 很多经典的AI算法都是树搜索,机器学习中的decision tree也是树结构

8.5 二叉树的性质

  1. 在二叉树上的第i层上至多有2i-1个节点(i>0)
  2. 深度为k的二叉树至多有2k-1个节点(k>0)
  3. 对于任意一颗二叉树,如果其子叶节点数为N0,而度数为2的节点总数为N2,则N0 = N2 + 1
  4. 最多有n个节点的完全二叉树的深度必为log2(n+1)
  5. 对于完全二叉树,若从上到下,从左到右编号,则编号为i的节点,其左孩子编号必定为2i,右孩子编号必为2i+1,其父节点的编号必为i//2 (i=1时为根,除外)

8.6 二叉树的遍历

  1. 广度优先遍历: 可以找到最短路径
  2. 深度优先遍历:

8.6.1 广度优先遍历

python">class Node(object):"""节点类"""def __init__(self, item):self.item = itemself.lchild = Noneself.rchild = Noneclass BinaryTree(object):"""完全二叉树"""def __init__(self, node=None):self.root = nodedef add(self, item):"""添加节点"""if self.root is None:self.root = Node(item)# 队列queue = []# 从尾部添加数据queue.append(self.root)while True:# 从头部取出数据node = queue.pop(0)# 判断左右子树是否为空if node.lchild is None:node.lchild = Node(item)returnelse:queue.append(node.lchild)if node.rchild is None:node.rchild = Node(item)returnelse:queue.append(node.rchild)def breath_traversal(self):"""广度优先遍历"""if self.root is None:return# 队列queue = []tree_list = []# 添加数据queue.append(self.root)while len(queue)>0:# 取出数据node = queue.pop(0)print(node.item)tree_list.append(node.item)# 判断左右子节点是否为空if node.lchild is not None:queue.append(node.lchild)if node.rchild is not None:queue.append(node.rchild)return tree_listif __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.add('j')tree.add('k')tree_list = tree.breath_traversal()print(tree_list)

8.6.2 深度优先遍历

  1. 先序优先遍历: 根 左 右
  2. 中序优先遍历: 左 根 右
  3. 后序优先遍历: 左 右 根
python">class Node(object):"""节点类"""def __init__(self, item):self.item = itemself.lchild = Noneself.rchild = Noneclass BinaryTree(object):"""完全二叉树"""def __init__(self, node=None):self.root = nodedef add(self, item):"""添加节点"""if self.root is None:self.root = Node(item)# 队列queue = []# 从尾部添加数据queue.append(self.root)while True:# 从头部取出数据node = queue.pop(0)# 判断左右子树是否为空if node.lchild is None:node.lchild = Node(item)returnelse:queue.append(node.lchild)if node.rchild is None:node.rchild = Node(item)returnelse:queue.append(node.rchild)def breath_traversal(self):"""广度优先遍历"""if self.root is None:return# 队列queue = []tree_list = []# 添加数据queue.append(self.root)while len(queue)>0:# 取出数据node = queue.pop(0)print(node.item)tree_list.append(node.item)# 判断左右子节点是否为空if node.lchild is not None:queue.append(node.lchild)if node.rchild is not None:queue.append(node.rchild)return tree_listdef preorder_traversal(self, root=None):"""先序遍历"""if root is not None:print(root.item, end='')self.preorder_traversal(root.lchild)self.preorder_traversal(root.rchild)def inorder_traversal(self, root=None):"""中序遍历"""if root is not None:self.inorder_traversal(root.lchild)print(root.item, end='')self.inorder_traversal(root.rchild)def postorder_traversal(self, root=None):"""后序遍历"""if root is not None:self.postorder_traversal(root.lchild)self.postorder_traversal(root.rchild)print(root.item, end='')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.add('j')tree.add('k')tree.preorder_traversal(tree.root)tree.inorder_traversal(tree.root)tree.postorder_traversal(tree.root)

http://www.ppmy.cn/embedded/129500.html

相关文章

人工智能技术的应用前景及其对生活和工作方式的影响

人工智能技术的应用前景及其对生活和工作方式的影响 随着人工智能&#xff08;AI&#xff09;技术的迅猛发展&#xff0c;其在各个领域的应用正日益深入&#xff0c;深刻改变着我们的生活和工作方式。本文将系统地探讨人工智能的历史、现状、未来应用前景&#xff0c;以及其对个…

微信小程序实现canvas电子签名

一、先看效果 小程序canvas电子签名 二、文档 微信小程序canvas 组件文档 微信小程序canvas API文档 H5Canvas文档 三、分析 1、初始话Canvas容器 2、Canvas触摸事件&#xff0c;bindtouchstart&#xff08;手指触摸动作开始&#xff09;、bindtouchmove&#xff08;手指触摸…

【ARM】ARM架构参考手册_Part A CPU(1)

目录​​​​​​​ 1.1 关于ARM架构 1.1.1 ARM寄存器 1.1.2 异常 1.1.3 状态寄存器 1.2 ARM指令集 1.2.1 分支指令 1.2.2 数据处理指令 算术/逻辑指令 比较指令 乘法指令 计算前导零指令 1.2.3 状态寄存器传送指令 1.2.4 加载和存储指令 加载和存储寄存器 加载…

c++就业 创建新的设计模式

virtual自然生成虚函数表&#xff08;一维数组记录了虚函数地址 通过偏移可以调相对应的方法&#xff09; vp 编译的时候地址自然会赋值给相对应的对象 如何体现多态 没有虚函数重写 那么就是早绑定 就比如subject会转换成base类型 p指向base对象 有虚函数就是晚绑定 p指向subj…

LlamaIndex核心概念查询管道(Query Pipelines)简介

LlamaIndex 查询管道简介 概述 LlamaIndex提供了一个声明性查询API&#xff0c;允许您将不同的模块链接在一起&#xff0c;以便在数据上编排从简单到高级的工作流。 这是以QueryPipeline抽象为中心的。装入各种模块&#xff08;从llm到提示符&#xff0c;再到检索器&#xf…

自由学习记录(12)

综合实践 2D的Shape&#xff0c;Tilemap都要导包的&#xff0c;编辑器也要导包&#xff0c;。。和2d沾边的可能3d都要主动导包 应该综合的去运用&#xff0c;不见得Tilemap就很万能&#xff0c;如果要做什么顶方块的有交互反应的物体&#xff0c; 那直接拖Sprite会更方便一些…

边缘计算网关助力煤矿安全远程监控系统

煤矿开采环境复杂&#xff0c;危险程度高&#xff0c;每一次事故都带给行业血淋淋的教训&#xff0c;安全问题也是政府与行业亟待解决的难题。伴随着技术的发展&#xff0c;煤矿智能化成为行业探索的新方向&#xff0c;降低安全风险也是智能化的重要目标之一。防微杜渐是安全生…

【OpenCV】人脸识别方法

代码已上传GitHub&#xff1a;plumqm/OpenCV-Projects at master EigenFace、FisherFace、LBPHFace 这三种方法的代码区别不大所以就一段代码示例。 EigenFace与FisherFace 1. 将人脸图像展开为一维向量&#xff0c;组成训练数据集 2. PCA&#xff08;EigenFace&#xff09;或…