算法[未完成]
算法
数据结构
数组
一个数组对象中存储着该数组内各个部分的索引,可根据索引直接查找到对应的值
链表
链表是由一个个节点组成的,每个节点根据链表类型不同结构也不同
单向链表:每个节点存储着自身的值(value)还有下一个节点的地址值
双向链表:每个除了有单向节点的属性外,还存储着前一个节点的地址值
队列
队列就跟一个排队一样,先进的数据先出队列,后进的数据后出队列
单向队列
每个队列的节点只能获取该节点的下一个节点,无法获取前一个节点
双端队列
每个队列的节点可以获取前一个节点及后一个节点
栈
栈是一种数据从尾部进入容器,然后数据从尾部取出的数据结构,其数据是先进后出,后进先出
堆
堆是一种基于 树 的数据结构,通常由完全二叉树(只有二叉树的最底层可以不填满的二叉树)实现
特性:
大顶堆:父节点的值总比子节点的值大
小顶堆:父节点的值总比子节点的值小
二叉树
二叉树
二叉树即为多个数据节点组成的数,从顶向下依次分2个岔,每个节点都可以有至多2个节点,一次向下延伸
最顶层的节点为根节点,最底层无子节点的节点为叶子节点
每次对二叉树添加新节点时,可随意位置添加
二叉树遍历
层序遍历:
依次从该节点向下,从左到右遍历每个节点
前序遍历:
先访问该节点,然后左子树,最后右子树,每个节点当其左子树访问结束才会方位其右子树
中序遍历:
先访问左子树,然后访问该节点,最后右子树
后续遍历:
先访问左子树,然后访问右子树,最后该节点
完全二叉树
只有二叉树的最底层可以不填满
每次添加新的节点需要从底层左端未填满处向右添加
二叉搜索树
二叉搜索树的每个节点多拥有了一个key值,该key值是唯一的,并且二叉搜索树是根据各个节点的key值来构建顺序的
任意节点,其key值比左节点key值大,比右节点小
以及,每次查找时,根据比较该查找节点key值的大小来查找,减少查询时间
自平衡二叉搜索树
二叉搜索树在插入和删除时,节点可能失衡
在插入或删除时,通过左右旋转,始终让二叉树保持平衡,称为自平衡的二叉搜索树
AVL是自平衡二叉树搜索树的实现之一
红黑树
红黑树也是一种自平衡二叉搜索树,与AVL数相比,红黑树插入和删除时,旋转次数更少
红黑树特性:
1.所有节点都有两种颜色:红与黑
2.所有null节点视为黑色
3.红色节点不能相邻[连在一起]
4.根节点是黑色
5.从根到任意一个叶子结点,路径中的黑色节点数一样(黑色完美平衡)[需要考虑null节点!]
插入规则:
插入节点均视为红色
1.插入节点为根节点,将根节点变黑
2.插入节点的父亲若为黑色,树的红黑性不变,无需调整
若插入节点的父亲为红色
1.叔叔[父节点的兄弟节点]为红色:
1.将父节点和叔叔节点都变为黑色
2.将祖父节点变为红色
3.把祖父节点视为新加入节点进行调整,依次递归
2.叔叔为黑色
父亲节点变黑,祖父节点变红,根据节点插入在左节点还是右节点以及父节点为左右孩子进行旋转
父左子左:祖父右旋,父左子右:父左旋,祖父右旋
父右子右:祖父左旋,父右子左:父右旋,祖父左旋
B树
B树用来存储磁盘内的数据,减少树高,利于磁盘内的读写
B树特性:
度 degree:指树中节点的孩子数
阶 order:指所有节点孩子数的最大值
1.除根节点和叶子节点外,每个节点至少有 (阶/2)个孩子
2.所有叶子节点都在同一层(高度相同)
3.每个节点都有多个key,以及多个孩子, 孩子数不超过key+1个
4.多个key值是递增的,像二叉树一样,不过 是 每个key值之间存放着下一个节点,该节点的key值在父节点key值之间
插入:
每个key都插向叶子节点,每次插入完成后检查子节点是否满了(出现上溢出)(孩子数达到阶数)
当满了就对该节点进行分割,分割: 以该节点中间key作为分割点,分割点上移到父节点,分割点右边的key独立出来为一个节点
为分割点右子节点
若根节点出现上溢出:
以分割点分割处3个节点,分割点作为新的父节点,左右2个节点作为根节点的子节点
删除:
删除非叶子节点元素都转换为删除叶子节点元素(将删除元素与左子树的前继节点key值交换,或右子树的后继节点key值交换)
下溢出:
当删除叶子节点元素后出现下溢出(叶子节点key的个数小于 阶数/2 -1)
1.当兄弟节点个数足够借,则借--借左右旋,借右左旋
2.当兄弟节点个数不足够,则合并,合并可能会导致父节点下溢出
父元素的处的key值下移到左节点,然后右节点合并过来
父元素为根节点: 依旧父元素key下移动,不过合并后根节点变为合并后的节点,也就是树高降低
哈希表
定义:
哈希表是一个数组,是一个可扩容的数组,哈希表内的每个元素有key值以及value值
哈希表会根据key值通过特定算法计算出一个根据key值恒定的值,该值为哈希值
在查找时,会根据元素的key值计算朱哈希值直接定位到对应元素,时间复杂度为 O(1)
哈希算法:
1.线性探测法
2.平方探测法
3.拉链法
扩容:
当 元素个数/数组长度 大于设定的某个值(装填因子) 会进行扩容
图
定义:
图是由顶点和边组成的数据结构
图分为有向和无向,有向图的边是单向的,无向图的边是双向的
度: 度为与该顶底相邻边的数量,在有向图中还细分为入度和出度
权: 边可以有权重,代表从源顶点到目标顶点的距离,费用,时间等度量
路径: 路径为从一个顶点到另一个顶点之间的一系列连续边
环: 在有向图中,从一个顶点开始,可以通过若干条有向边返回到该顶点,就形成一个环
图的表示:
邻接矩阵:
使用一个二维数组来表示,如果有链接对应的值为1,反之则为0
a b c d
a 0 1 0 1
b 1 0
c
d
链接表:
这里的指向不是代表路径,而是都是与该节点相连的
a->b->c [a与b,c都相连]
b->a->d
遍历:
深度优先搜索(DFS):
遍历的时候尽可能走的更远,走到头时,回到前一个顶点查看是否有未走路径,有就继续无责继续返回,直到所有顶点遍历完毕
广度优先搜索(BFS):
从最初顶点开始,所有路径同时遍历
拓扑排序
Dijkstra算法
Bellman-Ford算法
Floyd算法
最小生成树-prim算法
最小生成树-Kruskal
并查集
查找算法
二分查找
适用于以大小排序的一组数据,每次从数据的中间查找
def binary_search(arr,target):
i = 0
j = len(arr) -1
while i <= j :
index = int((i + j)/2)
m = arr[index]
if m >target:
j = index -1
elif m < target:
i = index +1
else:
return index
return -1
递归算法
递归就是讲一个大的问题拆成小问题解决,调用一个方法,该方法内部有调用其本身,在不断的调用自身时,缩小问题的范围,最终解决问题
递归需要一个终止点,达到终止点时,问题就结束
#以下是一个遍历的递归例子
def iteritor(node):
if node==None:
return
print(node.value)
iteritor(node.next)
多路递归
多路递归就是一个问题需要以多个子问题来解决
例:斐波那契数列
f(n) = { 0;n=0
1;n=1
f(n-1)+f(n-2); n>1}
#多路递归实现求得斐波那契数列第n项值
def mutiple_recursion(n):
if n =1:
return 1
elif n=0:
return 0
return mutiple_recursion(n-1) + mutiple_recursion(n-2)
#引入记忆法,减少时间复杂度
def mutiple_recursion(n,cache):
if cache[n] != -1:
return cache[n]
if n =1:
return 1
elif n=0:
return 0
result = mutiple_recursion(n-1) + mutiple_recursion(n-2)
cache[n] = result
return result
排序算法
冒泡排序
每轮冒泡不断的比较相邻的两个元素,如果是逆序的,则交换它们的位置
下一轮冒泡可以调整未排序的右边界,减少不必要的比较[每次比较,都至少会把最大的元素排列到最后]
def bubbleSort(arr):
n = len(arr) -1
while n != 0:
x = 0
for i in range(n):
if arr[i] > arr[i+1]:
arr[i],arr[i=1] = arr[i+1],arr[i]
x = i
n = x
选择排序
每一轮选择,找出最大或最小的元素,把它交换到合适的位置
def sort(arr):
len = len(arr)
for i in range(len - 1,-1,-1):
max = i
for j in range(i):
if arr[j] > arr[max]:
max = j
if max != i:
arr[max],arr[i] = arr[i],arr[max]
堆排序
将元素建立成一个大顶堆,每次将堆顶的元素移动交换到末尾,调整堆顶元素,让其重新符合大顶堆特性
插入排序
将数组分成2个数组,一个为有序数组,另一个为无序数组
遍历无序数组内的元素,依次按序插入数组中
希尔排序
希尔排序是将一个数组按一定间隔分成多个小组,在对这多个小组进行插入排序
然后减小间隔,继续循环,直到间隔为1,最后进行一次完整的插入排序,即可
间隔:一般是数组总长的一半,质数偶数都可以
归并排序
归并排序将数组先分为多个小部分排序,在依次将排序后的各个数组再次排序,最终完成排序
如[7,3,5,9,2,1,3]->[3,7,5,9,1,2,3]->[3,5,7,9,1,2,3]->[1,2,3,3,5,7,9]
def sort(arr):
n = len(arr)
a2 = []
width = 1
while width < n:
left = 0
while left < n:
rigth = max(left+2*width,n -1)
m = max(left+width-1,n-1)
merge(arr,left,m,m+1,right,a2)
left += 2*width
width *=2
a1[0,n] = a2[0,n]
快速排序
单边排序:
选择最右侧的元素最为基准点,设立2个变量i,j分别指向比基准点大,小的值
i,j都从左开始递增,当i遇到大值时,停止递增,指向基准点,待j递增遇到小值时,两者指向的元素交换,i,j都++
最后基准点与i指向的元素交换
双边排序:
选择最左侧的元素作为基准点,设立2个变量i,j分别指向比基准点大,小的值
i从左向右,j从右向左,先递增j,j找到时再递增i,当2者都找到时,二者指向的元素交换位置
最后基准点与i交换,i即为基准点最终索引
循环进行排序,每次排序都会缩小范围,分块进行排序,在排序元素只有一个元素时,返回
计数排序
1.先找到数组中值最大的元素
2.然后创建一个长度为该最大值元素值+1的新数组
3.遍历原数组,新数组的索引对应着原数组各个值,新数组的每个值都是该索引在原数组出现的次数
4.最后遍历新数组,更具新数组的索引(即原数组的元素)及对应值(出现次数),生成排序后的数组(该数组即为排序后的数组)
def sort(arr):
num = max(arr) + 1
count = [0]*num
result = []
for i in arr:
count[i] += 1
t = 0
while t<num:
for i in range(count[t]):
rusult.append(t)
t+=1
桶排序
将元素按值分段,每个段单独排序,最后在合并所有段即可
例:
将人的年龄分成10段,遍历年龄数组,每个元素/10 根据得到的值分段放入数组中[值为1一个数组,2为一个数组,,,,,]
基数排序
依次从元素的个位,十位等排序
每次位数的排序都放到一个有0到9的桶中,按大小放入,按顺序放入
遍历完原数组之后,将桶
例:
[96,214,5437,341,1245] -> [341,214,1245,96,5437] -> [214,5437,341,1245,96] -> [96,214,341,1245,5437]
贪心算法
定义:
在对问题求解时,总是做出在当前看来是最好的选择。也就是说,不从整体最优上加以考虑,算法得到的是在某种意义上的局部最优解 。
贪心算法实际应用---零钱找回问题
这个问题在我们的日常生活中很普遍。
假设1元、2元、5元、10元、20元、50元、100元的纸币分别有
c0, c1, c2, c3, c4, c5, c6张。现在要用这些钱来支付K元,至少要用多少张纸币?
用贪心算法的思想,很显然,每一步尽可能用面值大的纸币即可。在日常生活中我们自然而然也是这么做的。在程序中已经事先将Value按照从小到大的顺序排好。
动态规划
dynamic-programming:
programming:在这里指 用数学方法来根据子问题求解当前问题(通俗理解就是找到递推公式)
dynamic:指缓存上一步结果,根据上一步结果计算当前结果(多阶段进行)
合在一起:
找出递推公式,将当前问题分解成子问题,分阶段进行求解
求解过程中缓存子问题的解,避免重复计算
例:
斐波那契数列
def fibonacci(n):
arr = {}
arr[0] = 0
arr[1] = 1
if n == 0:
return 0
if n == 1:
return 1
for i in range(2,n):
arr[i] == arr[i-1] + arr[i-2]
return arr[n]

浙公网安备 33010602011771号