一、关于二叉树和堆的基本概念
1、二叉树
每个节点,最多有2个子树的数结构。
左右子树,也是最多有2个子节点。
2、满二叉树
除最后一层外,每个节点都有2个子节点。
3、完全二叉树
存在的节点,和满二叉树的节点完全对应。
4、堆:
Max Heap:最大的元素永远在根节点
任一非终端节点数据均不小于其左、右孩子节点。
Min Heap: 最小的元素永远在根节点
任一非终端节点数据均不大于其左、右孩子节点。
5、堆的特点:
i 表示索引,用公式找到索引所在节点的父节点和左右孩子节点的索引。
parent = (i-1)//2
c1 = 2*i+1
c2 = 2*i+2
二、堆排序的核心思想
以Max Heap 为例:
第一步:建堆,保证最大元素是堆顶
第二步:把堆顶元素和最后一个元素位置交换
第三步:调整堆结构,使堆满足 Max Heap ,调整的时候不再考虑最后一个位置元素
第四步:再次将堆顶元素和调整后堆的最后一个元素位置交换
第五步:调整堆结构
第六步:循环
考虑实现过程:
1.如何让数组满足堆的定义:建堆
构建堆的过程:
1)从堆的最后一个非叶子节点开始,进行调整
对于长度为n的堆,确定最后一个非叶子节点的位置:n//2-1
调整过程:在最后一个非叶子节点 的左右子节点中,找到最大的元素,交换位置
2)找到倒数第二个非叶子节点,不满足Max需要调整,满足则不调整
3)一直找到最后一个非叶子节点进行调整,最后满足了Max Heap的条件
堆顶元素和最后一个元素,交换。
交换后破坏了原有堆的结构。
然后缩小堆的规模,继续调整成Max Heap。
2. 调整完以后,继续交换
三、代码
#调整堆结果 def heapify(arr,action_node_index,length): ''' arr:要传入的数组 action_node_index:待调整的堆的节点的索引 length :堆的规模 ''' left_child_index = 2*action_node_index+1 right_child_index = 2*action_node_index+2 max_value_index = action_node_index #分别和左右子节点比较,找到最大的值对应的index #在查找的堆的范围内 if left_child_index<length and arr[left_child_index]>arr[max_value_index]: max_value_index = left_child_index if right_child_index<length and arr[right_child_index]>arr[max_value_index]: max_value_index = right_child_index #如果最大值的索引发生了变化,交换位置 if max_value_index!=action_node_index: arr[max_value_index],arr[action_node_index]=arr[action_node_index],arr[max_value_index] #因为位置的交换,可能导致交换后的堆不满足堆的结构 #继续调整基于该节点为根的堆结构 heapify(arr,max_value_index,length) #建堆:从最后一个非叶子节点 def buildheapify(arr): for i in range(len(arr)//2-1,-1,-1):#左必右开,到0索引结束 heapify(arr,i,len(arr)) #堆排序 def heapifySort(arr): length = len(arr) buildheapify(arr) for i in range(length): #做交换 arr[0],arr[length-i-1]=arr[length-i-1],arr[0] #调整堆 heapify(arr,0,length-1-i) return arr arr = [-111,12,0,-6,12,88,34,8,-3] print(heapifySort(arr)) #结果 [-111, -6, -3, 0, 8, 12, 12, 34, 88]
2、时间复杂度:O(nlogn)