文章目录
1.动态数组
概念:可以动态扩容 在数组的长度满了之后可扩容至两倍大小
时间复杂度: O(N)
三种分析方法:
aggregate method
amortized method
physicist method
2.优先队列(堆的应用)
-
堆的性质: 父节点总比子节点的值要大
可以按照一定的顺序取出数字 -
有两种基本操作 这两种基本操作可体现出二叉堆的优势
extractmax(x)
insert(x)
-
数的高度定义有两种:
edge 和 节点高度
- 完整二叉树(二叉堆)的定义:
最后一个等级(level)从左到右不能缺失
二叉完整树的最大高度,等比数列,总和等
最大高度:
假设root节点为LeveL1 则 第l个level共有
节点数共有
个
- 应用
找第K大元素、排序
基本操作 sift_down sift_Up等函数的Python代码实现
课程内容介绍了二叉堆的基本操作(insert,extrcatmax,siftup,siftdown,remove,buildheap)等
所有操作的时间复杂度都为
##二元堆的基本操作
class binary_heap():
def __init__(self,array):
self.array = array
self.size = len(array)
def siftup(self,i):
#偽代碼
# while i > 1 and H[Parent(i )] < H[i ]:
# swap H[Parent(i )] and H[i ]
# i ← Parent(i )
while (i > 0) and self.array[self.parent(i)] < self.array[i]:
self.array[self.parent(i)],self.array[i] = self.array[i],self.array[self.parent(i)]
i = self.parent(i)
def siftdown (self,i):
#偽代碼
# maxIndex ← i
# ℓ ← LeftChild(i )
# if ℓ ≤ size and H[ℓ] > H[maxIndex]:
# maxIndex ← ℓ
# r ← RightChild(i )
# if r ≤ size and H[r ] > H[maxIndex]:
# maxIndex ← r
# if i ̸= maxIndex:
# swap H[i ] and H[maxIndex]
# SiftDown(maxIndex)
maxindex = i
l = self.leftchild(i)
if (l <= self.size -1) and (self.array[l] > self.array[maxindex]):
maxindex = l
r = self.rightchild (i)
if (r <= self.size -1 ) and (self.array[r] > self.array[maxindex]):
maxindex = r
if (i != maxindex):
self.array[maxindex], self.array[i] = self.array[i], self.array[maxindex]
self.siftdown(maxindex)
def insert(self,P):
self.size += 1
self.array.append(P)
self.siftup(self.size-1)
def extractmax(self):
result = self.array[0]
self.array[0] = self.array[-1]
# del self.array[-1]
#改self.array试试
self.array = self.array[:-1]
self.siftdown(0)
return result
def remove(self,i):
self.array[i] = float("inf")
self.siftup(i)
self.extractmax(i)
def build_heap(self):
for i in range(self.size//2-1,-1,-1):
self.siftdown(i)
# print(self.array)
def heap_sort(self):
self.build_heap()
a = self.size
for i in range(a-1):
self.array[0], self.array[self.size-1] = self.array[self.size-1], self.array[0]
self.size -= 1
self.siftdown(0)
def parent(self,i):
return (i-1)//2
def leftchild(self,i):
return 2*i + 1
def rightchild(self,i):
return 2*i +2
#面向对象编程的好处和优势: 对一个对象可以反复地进行修改和操作,self定义了很多class内部的全局变量
#使得外部的代码看起来简洁明了 比如heap_sort当中优势就十分明显 self.size变量减小不用传递给另外的函数
#在对象内部使用自身的函数时需要加self
#在传入的是可变变量时,给同名变量赋值会在内部产生一个新的变量,脱离原来变量的关系
优势:
1.速度快(基本操作比数组快的多)
2.节省空间 (可在数组中排序实现)
3.实现简单
3 时间复杂度大于等于 排序算法比较
冒泡排序,选择排序:
时间复杂度 :
空间复杂度 :
可以在数组本身内实现
归并排序
时间复杂度 :
空间复杂度 :
二叉堆排序:
时间复杂度:
空间复杂度 : 可
也可
快速排序:
时间复杂度 :
空间复杂度 :
二叉堆排序的优点:
堆的最坏情况就是
而快排是平均情况是
4.disjoint set (不交集)
-
核心结构:
集合 —————— 集合内部的元素互斥
如 {3,4,6,8}是集合 {8,7,9,9}不是集合 -
基本操作:
创建一个包含元素i集合
makeset(i)
查找包含元素i的集合
id = find(i)
合并两个包含元素i和元素j集合
union(i,j) -
有效率的创建方法:
用数状结构 -
数的最高高度(height)为
所以union和find的时间复杂度都为 -
不交集的应用:
集合
不交集基本操作的代码实现
class disjoint:
def __init__(self,node_num):
###创建disjoint
self.parent = []
for i in range(node_num):
self.parent.append(i)
#创建树的高度
self.ranks = [1]*node_num
def find(self,i):
if (self.parent[i] != i):
return find(self.parent[i])
else:
return i
def union(self,a,b):
a_root = self.find(a)
b_root = self.find(b)
if (self.ranks[a_root] < self.ranks[b_root]):
self.parent[a_root] = b_root
elif (self.ranks[a_root] == self.ranks[b_root]):
#两个树高度相等,则总体树的高度加1
self.parent[a_root] = b_root
self.ranks[b_root] += 1
else:
self.parent[b_root] = a_root
4. 部分作业分析
选择部分
在以1为开始索引里,左子树的索引是 2*i, 而右子树要注意检查边界是否超界,所以是C
创建一个堆可以在
之内完成,但对这个堆排序的时间是
编程部分:
第一题:堆的基本操作,见上面操作代码
第二题: parallel_processing
一个给了worker数组,一个job的时间数组,按job的时间分配给worker去做
方法1:按照给定的代码结构去做,历遍job,再历遍worker的end_time,这样时间复杂度是
方法2 : 建一个二元数组
实际上end_time这一列一开始就是一个已经排好的二元堆(因为都是0)
所以问题就转换成了二元堆的提取最小值问题,
对于每一个job, 提取二元堆中的最小值之后对这个worker加上这个job的time然后sifdown就好了,
时间复杂度时
第三题:不交集的基本操作实现 union