树的一些基础概念、堆和 python中heapq模块使用简介

树定义:

树是一种数据结构,比如:目录结构。 

树是由n个节点组成的集合:

        n = 0,那么是一颗空树;

        n > 0, 那么存在一个节点为书的根节点,其他节点可分为m个子集,每个子集又为一棵子树。

                             

树的一些基础概念、堆和 python中heapq模块使用简介_第1张图片

关于树的一些概念:

根节点:例 A

叶子节点:不能分叉的节点,例 B

树的深度:4 (共有4层)

节点的度:节点有几个分支,(有两个孩子节点,那么度为2)

树的度:节点度的最大值。A有6个分支,为此树中节点最大的度,所以此树的度为6.

孩子节点/父节点:例A-父节点,B,C,D,E,F,G--子节点 或者 E---父节点,I J子节点

子树:E,I,J,P,Q(下图中红色圈出)

树的一些基础概念、堆和 python中heapq模块使用简介_第2张图片

 二叉树

 二叉树定义:度不超过2的树。每个节点最多有两个孩子节点,两个孩子节点被区分为左孩子节点和右孩子节点。

树的一些基础概念、堆和 python中heapq模块使用简介_第3张图片

完全二叉树 

满二叉树:一个二叉树,如果每一层的节点数都达到了最大值,那么这个二叉树就是满二叉树。

完全二叉树:叶子节点只能出现在最下层和次下层,并且最下面一层的节点都集中在该层的最左边。

 例下图:(a)满二叉树;(b)完全二叉树;(c)(d)非完全二叉树。

树的一些基础概念、堆和 python中heapq模块使用简介_第4张图片

定义:一种特殊的完全二叉树结构

        大根堆:一棵完全二叉树,满足任意节点都比起孩子节点大

        小根堆:一棵完全二叉树,满足任意节点都比起孩子节点小

树的一些基础概念、堆和 python中heapq模块使用简介_第5张图片

堆的向下调整:

堆的一次向下调整:假设左右子树都是堆,但自身不是堆,如下图,红色圈出的子树为堆。

树的一些基础概念、堆和 python中heapq模块使用简介_第6张图片

 当根节点的左右子树都是堆时,可以通过一次的向下调整来将其变成一个堆。(如上图调整成下图)

树的一些基础概念、堆和 python中heapq模块使用简介_第7张图片

#向下调整
def sift(data, low, high):
    i = low #父节点 
    j = 2 * i + 1  #左孩子节点, 2 * i + 1 右孩子节点
    tmp = data[i]
    while j <= high: #未超出堆的范围
        if j < high and data[j] < data[j + 1]: #左孩子节点小于右孩子节点
            j += 1
        if tmp < data[j]:
            data[i] = data[j]
            i = j
            j = 2 * i + 1
        else:
            break
        data[i] = tmp

 堆排序过程:

1. 建立堆;

2. 得到堆顶元素,为最大元素;

3. 去掉堆顶,将对的最后一个元素放到堆顶,此时可通过一次调整使堆重新有序;

4. 堆顶最大元素为第二大元素;

5. 重读步骤3,直到堆变空。

以大根堆为例,构造大根堆,从最后一个子树开始,让每个子树有序。

树的一些基础概念、堆和 python中heapq模块使用简介_第8张图片

n = len(data)
for i in range(n // 2 - 1, -1, -1):
    sift(data, i, n-1)

在构建好的大根堆基础上,使用向下调整对列表进行排序。每次出现最大元素。

堆排序时间复杂度O(nlogn),向下调整复杂度O(logn),heap_sort主程序时间复杂度O(n)

堆排序实现过程整体代码如下:

#向下调整
def sift(data, low, high):
    i = low #父节点
    j = 2 * i + 1  #左孩子节点
    tmp = data[i]
    while j <= high: #未超出堆的范围
        if j < high and data[j] < data[j + 1]: #左孩子节点小于右孩子节点
            j += 1
        if tmp < data[j]:
            data[i] = data[j]
            i = j
            j = 2 * i + 1
        else:
            break
        data[i] = tmp

#堆排序
def heap_sort(data):
    n = len(data)
    for i in range(n // 2 - 1, -1, -1):
        sift(data, i, n-1)         #构建大根堆
    for i in range(n - 1, -1, -1):
        data[0], data[i] = data[i], data[0] #将最后一个元素与堆顶元素交换(最大元素)保存起来
        sift(data, 0, i-1)   #对交换后的树进行向下调整,使其成为大根堆(不算存起来的元素)
        return data

data = [1,3,5,7,4,2,9,6,8]
print(data)
print(heap_sort(data))

#输出
[1, 3, 5, 7, 4, 2, 9, 6, 8]
[1, 2, 3, 4, 5, 6, 7, 8, 9]

heapq模块

python内置模块 --- heapq:实现了一个小根堆。

模块内常见的操作函数:

1. heapify(x) :    将列表x转换为堆(小根堆)。

2. heappush(heap,item): 将item压入堆中。(heap使存放堆的数组)

3. heappop(heap): 从堆中弹出最小的项,并将其值返回。

 根据上述常用函数,使用heapq有两种方式创建堆,一种是使用空列表,然后使用heapq.heappush()函数把值加入堆中,另一种是使用heapq.heapify(list),将列表转换为堆结构

代码示例:

import heapq

#第一种
data = [1,3,5,7,4,2,9,6,8]
heap = []   
for i in data:
    heapq.heappush(heap, i) #将元素加入堆中
print([heapq.heappop(heap) for _ in range(len(data))])

#输出
[1, 2, 3, 4, 5, 6, 7, 8, 9]



#第二种
data = [1,3,5,7,4,2,9,6,8]
print('data',data)
heapq.heapify(data)   #构建小根堆
print('data_heap',data)
print([heapq.heappop(data) for _ in range(len(data))]) #将有序弹出的小根堆元素组成列表

#输出
data [1, 3, 5, 7, 4, 2, 9, 6, 8]
data_heap [1, 3, 2, 6, 4, 5, 9, 7, 8]
[1, 2, 3, 4, 5, 6, 7, 8, 9]

上述第一种方式还可堆元组使用,排序针对元组中第一个元素。代码示例如下:

#对元组使用
data = [('a', 2),('g', 5), ('c',8), ('f',3)]
heap = []
for i in data:
    heapq.heappush(heap, i) #将元素加入堆中
print([heapq.heappop(heap) for _ in range(len(data))])

#输出
[('a', 2), ('c', 8), ('f', 3), ('g', 5)]




data = [('a', 2),('g', 5), ('c',8), ('f',3)]
heap = []
for i in data:
    heapq.heappush(heap, (i[1], i[0])) #将元素加入堆中
print([heapq.heappop(heap) for _ in range(len(data))])

#输出
[(2, 'a'), (3, 'f'), (5, 'g'), (8, 'c')]

常用来解决问题:topk问题

解决思路:

        1. 取列表前k个元素建立一个小根堆。堆顶就是目前第k大的数。

        2. 依次向后遍历原列表,对于列表中的元素,如果小于堆顶,则忽略该元素;如果大于 堆顶,则将堆顶元素更换为该元素,并且对堆进行一次调整。

        3. 遍历列表所有元素后,倒叙弹出堆顶。

你可能感兴趣的:(刷题,python,算法)