树是一种数据结构,比如:目录结构。
树是由n个节点组成的集合:
n = 0,那么是一颗空树;
n > 0, 那么存在一个节点为书的根节点,其他节点可分为m个子集,每个子集又为一棵子树。
根节点:例 A
叶子节点:不能分叉的节点,例 B
树的深度:4 (共有4层)
节点的度:节点有几个分支,(有两个孩子节点,那么度为2)
树的度:节点度的最大值。A有6个分支,为此树中节点最大的度,所以此树的度为6.
孩子节点/父节点:例A-父节点,B,C,D,E,F,G--子节点 或者 E---父节点,I J子节点
子树:E,I,J,P,Q(下图中红色圈出)
二叉树定义:度不超过2的树。每个节点最多有两个孩子节点,两个孩子节点被区分为左孩子节点和右孩子节点。
满二叉树:一个二叉树,如果每一层的节点数都达到了最大值,那么这个二叉树就是满二叉树。
完全二叉树:叶子节点只能出现在最下层和次下层,并且最下面一层的节点都集中在该层的最左边。
例下图:(a)满二叉树;(b)完全二叉树;(c)(d)非完全二叉树。
定义:一种特殊的完全二叉树结构
大根堆:一棵完全二叉树,满足任意节点都比起孩子节点大
小根堆:一棵完全二叉树,满足任意节点都比起孩子节点小
堆的一次向下调整:假设左右子树都是堆,但自身不是堆,如下图,红色圈出的子树为堆。
当根节点的左右子树都是堆时,可以通过一次的向下调整来将其变成一个堆。(如上图调整成下图)
#向下调整
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,直到堆变空。
以大根堆为例,构造大根堆,从最后一个子树开始,让每个子树有序。
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]
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')]
解决思路:
1. 取列表前k个元素建立一个小根堆。堆顶就是目前第k大的数。
2. 依次向后遍历原列表,对于列表中的元素,如果小于堆顶,则忽略该元素;如果大于 堆顶,则将堆顶元素更换为该元素,并且对堆进行一次调整。
3. 遍历列表所有元素后,倒叙弹出堆顶。