排序算法-堆排序

时间:2024-04-27 14:13:53

  一、二叉堆的特性:

1、最大堆的堆顶是整个堆中的最大元素。

2、最小堆的堆顶是整个堆中的最小元素。

      以最大堆为例,如果删除一个最大堆的堆顶(并不是完全删除,而是跟末尾的节点交换位置),经过自我调整,第2大的元素就会被交换上来,成为最大堆的新堆顶。 

二、堆排序

堆排序算法步骤:

1、把无序数组构建成二叉堆。

            从小到大排序,则构建成最大堆;

            从大到小排序,则构建成最小堆。

2、循环删除堆顶元素,替换到二叉堆的末尾,调整堆产生新的堆顶。

        第1步,把无序数组构建成二叉堆,这一步的时间复杂度是O (n)。

        第2步,需要进行n-1次循环。每次循环调用一次down_adjust方法,所以第2步的计算规模是(n-1) xlogn,时间复杂度为O ( nlogn) 。

        两个步骤是并列关系,所以整体的时间复杂度是O (nlogn)。

 如图所示:

 

def down_adjust(p_index,length,ll):
    '''
    下沉
    :param p_index:  父节点索引
    :param length:  大小
    :param ll:  数列
    :return:
    '''
    #保存父节点元素
    temp=ll[p_index]
    #左子节点索引
    sub_index=2*p_index+1
    #循环
    while sub_index <length:
        # 如果有右孩子,且右孩子的值大于左孩子的值,则定位到右孩子
        if sub_index+1<length and ll[sub_index+1]>ll[sub_index]:
            sub_index+=1
        # 如果父节点的值>=任何一个孩子的值,直接跳出
        if temp>=ll[sub_index]:
            break
        #无需真正交换
        #子节点元素赋给父节点元素
        ll[p_index]=ll[sub_index]
        #子节点索引重新赋值给父节点索引
        p_index=sub_index
        #子节点索引
        sub_index = 2 * sub_index + 1
    #父节点元素
    ll[p_index]=temp

def heapSort(ll):
    #1.把无序数组构成最大堆
    for i in range((len(ll)-2)//2,-1,-1):
         down_adjust(i,len(ll),ll)
    #2.循环删除堆顶元素,替换到二叉堆的末尾,调整堆产生新的堆顶。
    for i in range(len(ll)-1,0,-1):
        #最后一个与第一个元素交换
        t=ll[i]
        ll[i]=ll[0]
        ll[0]=t
        #下沉调整最大堆
        down_adjust(0,i,ll)

if __name__ == '__main__':
    ll = [10, 8,9, 7, 5, 4, 6, 3,2]
    print('排序前:')
    print(ll)
    # 调用方法排序
    heapSort(ll)
    print('排序后:')
    print(ll)

 

 相同点,堆排序和快速排序的平均时间复杂度都是O (nlogn),并且都是不稳定排序。

 不同点,快速排序的最坏时间复杂度是O (n2),而堆排序的最坏时间复杂度稳定在O (nlogn)。