【Python-AI篇】数据结构和算法
1. 算法概念
1.1 什么是数据结构
- 存储,组织数据的方式
1.2 什么是算法
- 实现业务目的的各种方法和思路
- 算法是独立的存在,只是思想,不依附于代码和程序,可以使用不同语言实现(java,python,c)
1.2.1 算法的五大特性
- 输入: 算法具有0个或多个输入
- 输出: 算法至少有1个或多个输出
- 有穷性: 算法在有限的步骤之后会自动结束循环而不会无限循环,并且每一个步骤可以在可接受的时间内完成
- 确定性: 算法中每一个步骤都有确定的含义,不会出现二义性
- 可行性: 算法的每一步都是可行的,也就是说每一步都能够执行有限的次数完成
穷举法: 将问题的答案一一列举,根据判断条件判断答案是否合适
# 如果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秒
方法二:
# 如果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 穷举法
- 循环次数
三次循环分别为:1001次 1001次 1001次 - 每次循环的步骤
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³ - 时间复杂度表示算法随着问题规模不断变化的最主要趋势, 衡量算法的量级
- 大O计法:将次要关系全部省略掉,由最高次项表示,最终生成一个表达式:T(n) = O(n³)
1.3.2 方法二
- T(n) = O(n2)
1.3.3 时间复杂度计算规则
- 基本操作: 时间复杂度为O(1)
- 顺序结构: 时间复杂度按加法计算
- 循环结构: 时间复杂度按乘法计算
- 分支结构: 时间复杂度取最大值
- 判断算法效率: 只需要关注操作数量的最高次项,其他次要项和常数项可忽略
- 在没有特殊说明时,我们所分析的算法时间复杂度都是指最坏时间复杂度
1.3.4 常见时间复杂度
举例 | 时间复杂度 | 非正式术语 | ||||||
---|---|---|---|---|---|---|---|---|
| | | ||||||
| | | ||||||
| | | ||||||
| | | ||||||
| | |
消耗时间从小到大: O(1) < O(logn) < O(n) < O(nlogn) < O(n2) < O(n3) < O(n!)
1.4 空间复杂度
- 空间复杂度(S(n))定义该算法所消耗的存储空间,临时占用存储空间大小的度量
2. 数据结构
- 概念: 存储、组织数据的方式,相互之间存在一种或多种特定关系的数据元素的集合,往往同高效的算法有关
- 作用: 相同的数据用不同的方式也就是不同的数据结构存储,那么带来的运行或存储效率也是不同的
示例
存储方式一:列表
people1 =[{"name":"aaa", "age":18}, {"name":"bbb","age":19}]
# 查找bbb
for peo in people1:if peo["name"] == "bbb":print("over")
时间复杂度为:O(n)
存储方式二:字典
people1 ={{"name":"aaa", "age":18}, {"name":"bbb","age":19}}
# 查找bbb
if "bbb" in people1:print("over")
时间复杂度为:O(1)
- 算法和数据结构的区别:
- 数据结构只是静态描述了数据元素的关系,高效的程序需要再在数据结构的基础上设计和选择算法
- 算法是为了解决实际问题而设计的,数据结构是算法需处理问题的载体
2.1 数据结构的分类
- 线性结构:表中各个节点具有线性关系(栈,队列)
- 非线性结构: 表中各个节点具有多个对应关系(树,图)
3. 顺序表
- 顺序表: 将元素顺序的放在一块连续的存储区里,元素间的关系由他们的存储顺序自然表示
- 链表: 将元素存放在通过链接构造起起来的一系列存储块中,存储区是非连续的
3.1 顺序表分类
- 一体式结构
- 分离式结构
- 无论是一体式结构还是分离式结构,在获取数据的时候直接通过下标偏移就可以找到数据所在空间的地址,而无需遍历后才可以获取地址,所以顺序表在获取地址操作的时间复杂度为:O(1)
- 顺序表完整信息:
- 数据区
- 信息区:即元素存储容量和当前表中已有元素的个数
- 顺序表的扩充
- 每次扩充增加固定数目和存储位置,如每次扩充10个位置,可称为线性增长
特点:节省空间,但扩充操作频繁,操作次数多 - 每次扩充容量加倍,如每次扩充增加一倍的存储空间
特点:减少了扩充的次数,但可能会浪费空间资源,以空间换取时间,推荐的方式
- 每次扩充增加固定数目和存储位置,如每次扩充10个位置,可称为线性增长
- 元素存储区替换:顺序表存储连续空间,如果下方扩充空间被占用,只能整体搬迁
3.2 顺序表增加删除元素
3.2.1 增加元素
- 尾端加入元素,时间复杂度为O(1)
- 非保序的加入元素(不常见),时间复杂度为O(1)
- 保序的元素加入,时间复杂度为O(n)
3.2.2 删除元素
- 尾端删除元素,时间复杂度为O(1)
- 非保序的删除元素(不常见),时间复杂度为O(1)
- 保序的元素删除,时间复杂度为O(n)
4. 链表
4.1 链表结构
- 单链表: 链表的一种形式,每个节点包含两个域,一个元素域和一个链接域,这个链接指向链表中下一个节点,而最后一个节点的链接域指向一个空值
- 表元素域elem:用来存放具体的数据
- 链接域next:用来存放下一个节点位置
- 变量head:指向链表的头结点的位置,从head出发能找到表中任意节点
# 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 链表的代码实现
---- 判空,长度,遍历,增加,删除
# 链表节点实现
# 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 栈的作用
- 计算机系统里面CPU结构的一部分
- 局部变量在函数使用完毕后销毁,存放进栈即不浪费空间,又能及时销毁
# 使用列表封装改造为栈,尾部操作效率更高
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 队列的作用
- 任务处理类系统:先把用户发送过来的请求存储在队列中,然后开启多个应用程序从队列中取任务进行处理,起到缓冲压力的作用
6.2 队列代码实现
# 使用列表封装改造成队列
# 创建一个空队列
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 双端队列代码实现
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. 算法
排序算法稳定性:在待排序的序列中,存在多个关键字记录,若经过排序,这些记录的相对次序保持不变,则称这种排序算法是稳定的,否则是不稳定的
- 不稳定的排序算法:选择排序,快速排序,希尔排序,堆排序
- 稳定的排序算法:冒泡排序,插入排序,归并排序,基数排序
7.1 冒泡排序
时间复杂度:最差:O(n2),最优O(n),算法稳定性:稳定
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),算法稳定性:不稳定
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 快速排序
基本思路:通过一趟排序将要排序的数据独立分割成两个部分,其中一部分的所有数据都比另一部分所有数据都要小,然后以此方法对这两部分的数据分别进行快速排序,整个排序过程可以递归执行,以此达到整个数据变成有序序列
排序流程如下:
- 首先设定一个分界值,通过分界值将数组分为左右两个部分
- 将大于或等于分界值的数据集中分布在右边,小于分界值的数据集中分布在左边,此时左边部分中各元素都小于分界值,右边部分各元素都大于等于分界值
- 左边和右边的数据可以独立排序,对于左侧数据,又可以取一个分界值,将该部分分为左右两部分,同样在左边放置最小值,右边放置最大值,右侧数据也做类似处理
- 重复上述步骤,可以看出这是一个递归定义,通过递归将左侧排好序后,再递归排好右侧部分顺序,当左右两个部分各数据排序完成后,整个数组排序也就完成了
时间复杂度:最差:O(n2),最优O(nlogn),算法稳定性:不稳定
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 插入排序
直接将一个数据插入到已经排好序的有序的数据中,从而得到一个新的,个数加一的有序数据,算法适用于少量数据排序
组成:
- 有序的数字(默认数组的第一个数字为有序的第一部分)
- 部分无序的数字(除了第一个数字之外的数字可以认为是无序的第二部分)
时间复杂度:最差:O(n2),最优O(n),算法稳定性:稳定
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 二分查找
折半查找,效率较高的查找方法
原理:
- 将数值分为三部分,中值前,中值和中值后
- 将要查找的值依次与中值进行比较,若小于中值则在中值前面找,若大于中值则在中值后面找等于中值则返回
- 必须采用顺序表存储结构,必须按关键字大小有序排列
时间复杂度:最差:O(logn),最优O(1),算法稳定性:稳定
7.5.1 递归版本
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 非递归版本
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)个有限结点组成一个具有层次关系的集合,把他叫做树
- 每个节点有零个或多个子节点
- 没有父节点的节点叫做根节点
- 每一个非根节点只有一个父节点
- 除了根节点以外,每个子节点可以分为多个不相交的树
8.1 树的相关术语:
- 节点的度: 一个节点含有子节点的个数
- 树的度: 一棵树中,最大节点的度称为树的度
- 叶节点或终端节点: 度为0的节点
- 父节点: 若一个节点含有子节点,则这个节点称为其子节点的父节点
- 子节点: 一个节点含有的子树的根节点称为该节点的子节点
- 兄弟节点: 具有相同父节点的节点互称兄弟节点
- 节点层次: 从根开始定义,根为第一层,根的子节点为第二层,以此类推
- 树的高度和深度: 树种节点的最大层次
- 堂兄弟节点: 父节点的同一层节点互称为堂兄弟节点
- 节点祖先: 从根到该节点所经历分支上的所有节点
- 子孙: 以某节点为根的子树中任意节点
- 森林: 由m(m>=0)棵互不相交的树的集合
8.2 树的种类和存储
- 无序树: 树中任意节点的子节点之间没有顺序关系,也称为自由树
- 有序树: 树中任意节点的子节点之间有顺序关系
- 霍夫曼树(用于信息编码): 带权路径最短的二叉树称为霍夫曼树或最优二叉树
- B树: 一种对读写操作进行优化的自平衡的二叉查找树,能够保持数据有序,拥有多于两个的子树
- 二叉树: 每个节点最多含有两个子树
- 完全二叉树: 对于一颗二叉树,假设其深度为d(d>1),除了第d层之外,其他各层的节点数目均已达到最大值,且d层所有节点从左到右连续的紧密排列,这样的二叉树被称为完全二叉树,其中满二叉树的定义是所有叶节点都在最底层的二叉树
- 平衡二叉树(AVL树): 当且仅当任何节点的两颗子树的高度差不大于1的二叉树
- 排序二叉树(二叉搜索树、有序二叉树): 要求:①若左子树不空,则左子树上所有节点的值均小于它的根节点的值;②若右子树不空,则右子树上所有节点的值均大于根节点的值;③左右子树分别也为排序二叉树;④排序二叉树包含空树;
8.3 二叉树的存储
- 顺序存储: 将数据结构存储在固定的数组中,虽然遍历上有优势,但是所占空间大,是非主流的二叉树存储方式
- 链式存储: 由于对节点的个数无法掌握,常见的树的存储表示都转换成二叉树进行处理,子节点最多个数为2,主流二叉树存储方式(指针域个数不定)
8.4 树的应用场景
- xml、html等,编写这些东西的解析器的时候,不可避免的需要用到树
- 路由协议就是使用了树的算法
- mysql数据索引
- 文件系统的目录结构
- 很多经典的AI算法都是树搜索,机器学习中的decision tree也是树结构
8.5 二叉树的性质
- 在二叉树上的第i层上至多有2i-1个节点(i>0)
- 深度为k的二叉树至多有2k-1个节点(k>0)
- 对于任意一颗二叉树,如果其子叶节点数为N0,而度数为2的节点总数为N2,则N0 = N2 + 1
- 最多有n个节点的完全二叉树的深度必为log2(n+1)
- 对于完全二叉树,若从上到下,从左到右编号,则编号为i的节点,其左孩子编号必定为2i,右孩子编号必为2i+1,其父节点的编号必为i//2 (i=1时为根,除外)
8.6 二叉树的遍历
- 广度优先遍历: 可以找到最短路径
- 深度优先遍历:
8.6.1 广度优先遍历
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 深度优先遍历
- 先序优先遍历: 根 左 右
- 中序优先遍历: 左 根 右
- 后序优先遍历: 左 右 根
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)
相关文章:

【Python-AI篇】数据结构和算法
1. 算法概念 1.1 什么是数据结构 存储,组织数据的方式 1.2 什么是算法 实现业务目的的各种方法和思路算法是独立的存在,只是思想,不依附于代码和程序,可以使用不同语言实现(java,python,c&a…...

VideoCLIP-XL:推进视频CLIP模型对长描述的理解
摘要 对比语言-图像预训练(CLIP)已被广泛研究并应用于众多领域。然而,预训练过程中对简短摘要文本的重视阻碍了CLIP理解长描述的能力。在视频方面,这个问题尤为严重,因为视频通常包含大量详细内容。在本文中ÿ…...

【vue】vue-router_ vue3路由管理器
代码获取 vue-router_ vue3路由管理器 ⼀、基本介绍 1. 单⻚应⽤程序介绍 1.1 概念 单⻚应⽤程序:SPA(Single Page Application)是指所有的功能都在⼀个HTML⻚⾯上实现 1.2 具体⽰例 单⻚应⽤⽹站: ⽹易云⾳乐 https://music.163.com/ 多⻚应⽤⽹…...

昇思MindSpore进阶教程--Diffusion扩散模型(上)
大家好,我是刘明,明志科技创始人,华为昇思MindSpore布道师。 技术上主攻前端开发、鸿蒙开发和AI算法研究。 努力为大家带来持续的技术分享,如果你也喜欢我的文章,就点个关注吧 正文 关于扩散模型(Diffusi…...

Nginx:proxy_pass指令
proxy_pass 指令在 Nginx 中是实现反向代理和负载均衡的重要指令。 一. 反向代理 在反向代理的场景下,proxy_pass 指令用于将接收到的请求转发给另一个后端服务器。后端服务器地址可以是 IP 地址加端口、域名加端口、或者一个完整的 URL。 注意事项 proxy_pass …...

【AI学习】Mamba学习(十):HiPPO总结
前面用五篇文章陆续学了HiPPO框架。 这里再进行一下总结。 总结 HiPPO,高阶多项式投影,high-order polynomial projection operators 为了解决从序列数据中建模和学习的问题,尤其是长序列,十万甚至百万长度的序列,使…...

AI编程新纪元:Cursor与V0引领的技术变革
#1024程序员节 | 征文# AI编程新纪元:Cursor与V0引领的技术变革 作为一名SAP业务顾问,虽然我懂一些ABAP开发,但是我对于前后端开发是完全不懂的,我一直对前后端开发怀有浓厚兴趣,总想着自己能开发出一些好玩的东西&…...

python——类
问:小编为什么突然开始发python?难道C语言你不行了? 废话少说,让我们进入python中的类的学习!! (一)基本知识 (1)掌握类的概念 1、类的定义: 即…...

走廊泼水节——求维持最小生成树的完全图的最小边权和
题目 思考 代码 #include <bits/stdc.h> using namespace std; const int N 6010; const int M N; int p[N], sz[N]; struct edge{int a;int b;int c;bool operator < (const edge& v) const{return c < v.c;} }e[M]; int find(int x) {if(p[x] ! x) p[x] …...

LC:动态规划-买卖股票
文章目录 121. 买卖股票的最佳时机122. 买卖股票的最佳时机 II714. 买卖股票的最佳时机含手续费309. 买卖股票的最佳时机含冷冻期 121. 买卖股票的最佳时机 链接:https://leetcode.cn/problems/best-time-to-buy-and-sell-stock/description/ 使用贪心,…...

FLINK SQL 任务参数
在Flink SQL任务中,参数配置对于任务的性能和稳定性至关重要。以下是对运行时参数、优化器参数和表参数的详细解析: 一、运行时参数 运行时参数主要影响Flink作业在执行过程中的行为。以下是一些关键的运行时参数: 并行度(Para…...

HCIP——以太网交换安全(四)DHCP Snooping
目录 一、DHCP Snooping的知识点 二、DHCP Snooping实验拓扑 三、总结 一、DHCP Snooping的知识点 1.1、DHCP snooping 概述: ①DHCP Snooping使能DHCP的一种安全特性,用于保证DHCP客户端从合法的DHCP服务端获取IP地址。DHCP服务器记录DHCP客户端IP…...

k8s worker 节点关机 sts 管理的 pod 无法迁移
背景 1.28.2 版本 k8s 中的一台 worker 节点内存异常,需要关机换内存,正好可以测试一下 pod 的迁移。 发现 deployment 管理的 pod 是能够重新创建飘到其他节点上的,但是 statefulset 管理的 pod 一直处于 Terminating 状态无法迁移&#…...

排序04 视频播放建模
视频播放时长 用p拟合y,t是用户的实际观看时长,用y和p熵作为损失函数,使得p接近y。 输出z,对z做sigmoid变换。 exp(z)可以视为对播放时长的预估 视频完播 回归方法 二元分类方法 调整:预估完播率不能直接使用...

【常见大模型API调用】第三篇:清华智谱--智谱AI
1. 公司及模型介绍 智谱AI是一家由清华大学计算机系知识工程实验室的技术成果转化而来的AI知识智能技术开发商。智谱AI致力于打造新一代认知智能大模型,专注于做大模型的中国创新。 2024年1月16日,智谱AI在首届技术开放日上发布了新一代基座大模型GLM-…...

LayerSkip – Meta推出加速大型语言模型推理过程的技术
我们提出的 LayerSkip 是一种端到端的解决方案,可加快大型语言模型(LLM)的推理速度。 首先,在训练过程中,我们采用了层间丢弃技术(layer dropout),早期层间丢弃率较低,后期层间丢弃率较高。 其次…...

环境变量与本地变量(Linux)
引言 在当今的计算机技术领域,Linux操作系统以其稳定性和灵活性而广受欢迎。它不仅是服务器和开发者的首选平台,也是探索计算机科学和系统编程的宝库。在这个强大的操作系统中,环境变量与本地变量扮演着至关重要的角色,它们是管理…...

【完-网络安全】Windows防火墙及出入站规则
文章目录 防火墙入站和出站的区别域网络、专用网络、公用网络的区别 防火墙 防火墙默认状态一般是出站允许,入站阻止。 入站和出站的区别 入站就是别人来访问我们的主机,也就是正向shell的操作 出站就是反向shell,主机需要主动连接kali&am…...

Vue学习记录之十七 css中样式穿透及新特征介绍
一、scoped原理 在vue页面的css中,有一个设置为scoped,使用以后dom的节点会出现下面的规则。其实我们打完包就是一个html页面,如果不做处理,将会导致css混乱。 给HTML的DOM节点加一个不重复data属性(形如:data-v-123)来表示他的唯一性在每句css选择器的末尾(编译后的生成的…...

Nature 正刊丨海洋涡旋中常见的地下热浪和寒潮
01摘要 由于全球变暖,极端海洋温度事件变得越来越普遍,造成了灾难性的生态和社会经济影响1,2,3,4,5。尽管基于卫星观测对表层海洋热浪(MHW)和海洋寒潮(MCS)进行了广泛的研究6,7,但我们对这些极…...

代码随想录算法训练营第六十二天| prim算法,kruskal算法
训练营六十二天打卡,图论比较难,坚持下来胜利就在眼前! 53.卡码网【寻宝】 题目链接 解题过程 没做过类似的题目,跟着答案敲了一遍最小生成树 可以使用 prim算法 也可以使用 kruskal算法计算出来。prim算法 是从节点的角度 采用…...

Newstar_week1_week2_wp
week1 wp crypto 一眼秒了 n费马分解再rsa flag: import libnum import gmpy2 from Crypto.Util.number import * p 9648423029010515676590551740010426534945737639235739800643989352039852507298491399561035009163427050370107570733633350911691280297…...

今天我们研究一段代码(异或位运算)
let a 18 // 甲 let b 20 // 乙a a ^ b b a ^ b a a ^ b console.log("a",a) // a 20 console.log("b",b) // b 18今天我们就研究上面这一段代码,简单解释一下,初始化一个a 18 b 20, 中间经过了三次的异或之后…...

pycharm中使用ctrl+鼠标滚轮改变字体大小
文章目录 pycharm使用ctrl鼠标滚轮改变字体大小1.打开pycharm选择file2.选择setting4.选择keymap,然后再右边的输入框中输入increase进行增大字体4.鼠标选择后,点击添加鼠标快捷方式,然后设置鼠标滚轮往上增大字体。5.设置缩小字体࿰…...

【算法-动态规划】打家劫舍专题
文章目录 1.打家劫舍1.1一维数组1.2三变量法1.3双数组法 2.打家劫舍22.1双数组法2.2 三变量法 3.打家劫舍33.1动态规划3.2双变量法 4.删除相邻数字的最大分数4.1双状态数组4.2一维数组4.3三变量法 1.打家劫舍 198. 打家劫舍 - 力扣(LeetCode) 1.1一维数…...

关于技术管理者的一些思考
前 言 在软件开发领域,当一名资深工程师有机会成为一名技术管理者的时候,通常他/她的反应是什么?兴奋、担扰、无奈还是推托,具体是什么心情也许对结果并不重要,更加重要是在一刻,我们一定要问问我们内心的…...

Alpha-CLIP: A CLIP Model Focusing on Wherever You Want CVPR 2024
在原始的接受RGB三通道输入的CLIP模型的上额外增加了一个alpha通道。在千万量级的RGBA-region的图像文本对上进行训练后,Alpha-CLIP可以在保证CLIP原始感知能力的前提下,关注到任意指定区域。 GitHub - SunzeY/AlphaCLIP: [CVPR 2024] Alpha-CLIP: A CLI…...

Golang | Leetcode Golang题解之第495题提莫攻击
题目: 题解: func findPoisonedDuration(timeSeries []int, duration int) (ans int) {expired : 0for _, t : range timeSeries {if t > expired {ans duration} else {ans t duration - expired}expired t duration}return }...

04 go语言(golang) - 变量和赋值过程
变量 在Go语言中,变量的定义和初始化是编程的基础部分。Go提供了多种方式来声明和初始化变量,以适应不同的使用场景。 基本变量声明 使用var关键字: 使用var关键字可以在函数内部或外部声明变量。如果在函数外部声明,该变量为全…...

语言/图像/视频模型一网打尽!BigModel大模型开放平台助力开发者轻松打造AI新应用!
2024年8⽉28⽇,在ACM SIGKDD(国际数据挖掘与知识发现⼤会,KDD)上会议现场,智谱AI重磅推出了新⼀代全⾃研基座⼤模型 GLM-4-Plus、图像/视频理解模型 GLM-4V-Plus 和⽂⽣图模型 CogView3-Plus。这些新模型,已…...