couresra课程 data structure 第二周 第三周笔记(含课程内容代码实现和排序算法比较)

1.动态数组

概念:可以动态扩容 在数组的长度满了之后可扩容至两倍大小
时间复杂度: O(N)

三种分析方法:

aggregate method
amortized method
physicist method

2.优先队列(堆的应用)

  • 堆的性质: 父节点总比子节点的值要大
    可以按照一定的顺序取出数字

  • 有两种基本操作 这两种基本操作可体现出二叉堆的优势
    extractmax(x)
    insert(x)

  • 数的高度定义有两种:

edge 和 节点高度

  • 完整二叉树(二叉堆)的定义

最后一个等级(level)从左到右不能缺失

二叉完整树的最大高度,等比数列,总和等
最大高度: O ( l o g ( N ) ) O(log(N))
假设root节点为LeveL1 则 第l个level共有 2 l 1 2^{l-1}个节点
节点数共有 2 l 1 2^l-1

扫描二维码关注公众号,回复: 8721224 查看本文章
  • 应用
    找第K大元素、排序

基本操作 sift_down sift_Up等函数的Python代码实现

课程内容介绍了二叉堆的基本操作(insert,extrcatmax,siftup,siftdown,remove,buildheap)等
所有操作的时间复杂度都为 O ( N l o g ( N ) ) O(Nlog(N))

##二元堆的基本操作
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 时间复杂度大于等于 O ( N ( l o g ( n ) ) O(N(log(n)) 排序算法比较

冒泡排序,选择排序:
时间复杂度 : O ( N 2 ) O(N^2)
空间复杂度 : C C 可以在数组本身内实现

归并排序
时间复杂度 : O ( N l o g ( N ) ) O(Nlog(N))
空间复杂度 : N N

二叉堆排序:
时间复杂度: O ( N l o g ( N ) O(Nlog(N)
空间复杂度 : 可 C C 也可 N N

快速排序:
时间复杂度 : O ( N l o g ( N ) ) O(Nlog(N))
空间复杂度 : N N

二叉堆排序的优点:
堆的最坏情况就是 O ( N l o g ( N ) ) O(Nlog(N))
而快排是平均情况是 O ( N l o g ( N ) O(Nlog(N)

4.disjoint set (不交集)

  • 核心结构:
    集合 —————— 集合内部的元素互斥
    如 {3,4,6,8}是集合 {8,7,9,9}不是集合

  • 基本操作:
    创建一个包含元素i集合
    makeset(i)
    查找包含元素i的集合
    id = find(i)
    合并两个包含元素i和元素j集合
    union(i,j)

  • 有效率的创建方法:
    用数状结构

  • 数的最高高度(height)为 l o g 2 n log_{2}{n}
    所以union和find的时间复杂度都为 l o g ( N ) log(N)

  • 不交集的应用:
    集合

不交集基本操作的代码实现

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
在这里插入图片描述
创建一个堆可以在 O ( N ) O(N) 之内完成,但对这个堆排序的时间是 O ( l o g ( N ) ) O(log(N))

编程部分:

第一题:堆的基本操作,见上面操作代码
第二题: parallel_processing
一个给了worker数组,一个job的时间数组,按job的时间分配给worker去做
方法1:按照给定的代码结构去做,历遍job,再历遍worker的end_time,这样时间复杂度是 O ( N 2 ) O(N^2)
方法2 : 建一个二元数组 [ [ w o r k e r , e n d t i m e ] ] [[worker,end_time]] 实际上end_time这一列一开始就是一个已经排好的二元堆(因为都是0)
所以问题就转换成了二元堆的提取最小值问题,
对于每一个job, 提取二元堆中的最小值之后对这个worker加上这个job的time然后sifdown就好了,
时间复杂度时 O ( N l o g ( N ) ) O(Nlog(N))
第三题:不交集的基本操作实现 union

发布了10 篇原创文章 · 获赞 0 · 访问量 94

猜你喜欢

转载自blog.csdn.net/xcpython/article/details/103966176