【码极客精讲】堆排序

堆排序(英语:Heapsort)是指利用堆这种数据结构所设计的一种排序算法。堆是一个近似完全二叉树的结构,并同时满足堆积的性质:即子结点的键值或索引总是小于(或者大于)它的父节点。 

堆的操作

在堆的数据结构中,堆中的最大值总是位于根节点(在优先队列中使用堆的话堆中的最小值位于根节点)。堆中定义以下几种操作:

  • 最大堆调整(Max Heapify):将堆的末端子节点作调整,使得子节点永远小于父节点

  • 创建最大堆(Build Max Heap):将堆中的所有数据重新排序

  • 堆排序(HeapSort):移除位在第一个数据的根节点,并做最大堆调整的递归运算 

  • 实现示例

    C语言

    1

    2

    3

    4

    5

    6

    7

    8

    9

    10

    11

    12

    13

    14

    15

    16

    17

    18

    19

    20

    21

    22

    23

    24

    25

    26

    27

    28

    29

    30

    31

    32

    33

    34

    35

    36

    37

    38

    39

    40

    41

    42

    43

    44

    45

    46

    47

    48

    49

    50

    51

    52

    53

    54

    55

    #include <stdio.h>

    #include <stdlib.h>

    void swap(int* a, int* b)

    {

        int temp = *b;

        *b = *a;

        *a = temp;

    }

    void max_heapify(int arr[], int start, int end) 

    {

        //建立父节点指标和子节点指标

        int dad = start;

        int son = dad * 2 1;

        while (son <= end)  //若子节点指标在范围内才做比较

            {

                if (son + 1 <= end && arr[son] < arr[son + 1]) 

                //先比较两个子节点大小,选择最大的

                son++;

            if (arr[dad] > arr[son]) //如果父节点大於子节点代表调整完毕,直接跳出函数

                return;

            else  //否则交换父子内容再继续子节点和孙节点比较

            {

                swap(&arr[dad], &arr[son]);

                dad = son;

                son = dad * 2 1;

            }

        }

    }

    void heap_sort(int arr[], int len) 

    {

        int i;

        //初始化,i从最後一个父节点开始调整

        for (i = len / 2 1; i >= 0; i--)

            max_heapify(arr, i, len - 1);

        //先将第一个元素和已排好元素前一位做交换,再重新调整,直到排序完毕

        for (i = len - 1; i > 0; i--) 

        {

            swap(&arr[0], &arr[i]);

            max_heapify(arr, 0, i - 1);

        }

    }

    int main() {

        int arr[] = { 353086158624947018973125974026 };

        int len = (int) sizeof(arr) / sizeof(*arr);

        heap_sort(arr, len);

        int i;

        for (i = 0; i < len; i++)

            printf("%d ", arr[i]);

        printf("\n");

        return 0;

    }

    C++

    1

    2

    3

    4

    5

    6

    7

    8

    9

    10

    11

    12

    13

    14

    15

    16

    17

    18

    19

    20

    21

    22

    23

    24

    25

    26

    27

    28

    29

    30

    31

    32

    33

    34

    35

    36

    37

    38

    39

    40

    41

    42

    43

    44

    45

    46

    #include <iostream>

    #include <algorithm>

    using namespace std;

      

    void max_heapify(int arr[], int start, int end)

    {

        //建立父节点指标和子节点指标

        int dad = start;

        int son = dad * 2 + 1;

        while (son <= end)  //若子节点指标在范围内才做比较

        {   

            if (son + 1 <= end && arr[son] < arr[son + 1]) //先比较两个子节点大小,选择最大的

                son++;

            if (arr[dad] > arr[son]) //如果父节点大於子节点代表调整完毕,直接跳出函数

                return;

            else  //否则交换父子内容再继续子节点和孙节点比较

            {

                swap(arr[dad], arr[son]);

                dad = son;

                son = dad * 2 + 1;

            }

        }

    }

      

    void heap_sort(int arr[], int len)

    {

        //初始化,i从最後一个父节点开始调整

        for (int i = len / 2 - 1; i >= 0; i--)

            max_heapify(arr, i, len - 1);

        //先将第一个元素和已经排好的元素前一位做交换,再从新调整(刚调整的元素之前的元素),直到排序完毕

        for (int i = len - 1; i > 0; i--)

        {

            swap(arr[0], arr[i]);

            max_heapify(arr, 0, i - 1);

        }

    }

    int main()

    {

        int arr[] = { 3, 5, 3, 0, 8, 6, 1, 5, 8, 6, 2, 4, 9, 4, 7, 0, 1, 8, 9, 7, 3, 1, 2, 5, 9, 7, 4, 0, 2, 6 };

        int len = (int) sizeof(arr) / sizeof(*arr);

        heap_sort(arr, len);

        for (int i = 0; i < len; i++)

            cout << arr[i] << ' ';

        cout << endl;

        system("pause");

    }

    Java语言

    1

    2

    3

    4

    5

    6

    7

    8

    9

    10

    11

    12

    13

    14

    15

    16

    17

    18

    19

    20

    21

    22

    23

    24

    25

    26

    27

    28

    29

    30

    31

    32

    33

    34

    35

    36

    37

    38

    39

    40

    41

    42

    43

    44

    45

    46

    47

    48

    49

    50

    51

    52

    53

    54

    55

    56

    57

    58

    59

    60

    61

        /**

        * 选择排序-堆排序

        * @param array 待排序数组

        * @return 已排序数组

        */

        public static int[] heapSort(int[] array) {

            //这里元素的索引是从0开始的,所以最后一个非叶子结点array.length/2 - 1

            for (int i = array.length / 2 1; i >= 0; i--) {  

                adjustHeap(array, i, array.length);  //调整堆

            }

      

            // 上述逻辑,建堆结束

            // 下面,开始排序逻辑

            for (int j = array.length - 1; j > 0; j--) {

                // 元素交换,作用是去掉大顶堆

                // 把大顶堆的根元素,放到数组的最后;换句话说,就是每一次的堆调整之后,都会有一个元素到达自己的最终位置

                swap(array, 0, j);

                // 元素交换之后,毫无疑问,最后一个元素无需再考虑排序问题了。

                // 接下来我们需要排序的,就是已经去掉了部分元素的堆了,这也是为什么此方法放在循环里的原因

                // 而这里,实质上是自上而下,自左向右进行调整的

                adjustHeap(array, 0, j);

            }

            return array;

        }

      

        /**

        * 整个堆排序最关键的地方

        * @param array 待组堆

        * @param i 起始结点

        * @param length 堆的长度

        */

        public static void adjustHeap(int[] array, int i, int length) {

            // 先把当前元素取出来,因为当前元素可能要一直移动

            int temp = array[i];

            for (int k = 2 * i + 1; k < length; k = 2 * k + 1) {  //2*i+1为左子树i的左子树(因为i是从0开始的),2*k+1为k的左子树

                // 让k先指向子节点中最大的节点

                if (k + 1 < length && array[k] < array[k + 1]) {  //如果有右子树,并且右子树大于左子树

                    k++;

                }

                //如果发现结点(左右子结点)大于根结点,则进行值的交换

                if (array[k] > temp) {

                    swap(array, i, k);

                    // 如果子节点更换了,那么,以子节点为根的子树会受到影响,所以,循环对子节点所在的树继续进行判断

                        i  =  k;

                            else {  //不用交换,直接终止循环

                    break;

                }

            }

        }

      

        /**

        * 交换元素

        * @param arr

        * @param a 元素的下标

        * @param b 元素的下标

        */

        public static void swap(int[] arr, int a, int b) {

            int temp = arr[a];

            arr[a] = arr[b];

            arr[b] = temp;

        }

    Python语言

    1

    2

    3

    4

    5

    6

    7

    8

    9

    10

    11

    12

    13

    14

    15

    16

    17

    18

    19

    20

    21

    22

    23

    24

    25

    26

    27

    28

    29

    30

    31

    32

    33

    def big_endian(arr,start,end):    

        root=start    

        child=root*2+1 #左孩子    

        while child<=end:

        #孩子比最后一个节点还大,也就意味着最后一个叶子节点了,就得跳出去一次循环,已经调整完毕     

            if child+1<=end and arr[child]<arr[child+1]:

            #为了始终让其跟子元素的较大值比较,如果右边大就左换右,左边大的话就默认           

                child+=1            

            if arr[root]<arr[child]:

            #父节点小于子节点直接交换位置,同时坐标也得换,这样下次循环可以准确判断:是否为最底层,

            #是不是调整完毕                

                arr[root],arr[child]=arr[child],arr[root]                

                root=child                

                child=root*2+1            

            else:               

            break

             

    def heap_sort(arr): #无序区大根堆排序    

        first=len(arr)//2 - 1    

        for start in range(first,-1,-1):

        #从下到上,从左到右对每个节点进行调整,循环得到非叶子节点        

            big_endian(arr,start,len(arr)-1) #去调整所有的节点    

        for end in range(len(arr)-1,0,-1):        

            arr[0],arr[end]=arr[end],arr[0] #顶部尾部互换位置        

            big_endian(arr,0,end-1) #重新调整子节点的顺序,从顶开始调整    

        return arr

         

    def main():    

        l=[3,1,4,9,6,7,5,8,2,10]    

        print(heap_sort(l))

    if __name__=="__main__":    

        main()

    PHP语言

    1

    2

    3

    4

    5

    6

    7

    8

    9

    10

    11

    12

    13

    14

    15

    16

    17

    18

    19

    20

    21

    22

    23

    24

    25

    26

    27

    28

    29

    30

    31

    32

    33

    34

    35

    36

    37

    38

    39

    40

    41

    42

    43

    44

    45

    function hsort(array &$arr, $len){

        $idx = $len - 1;

        //创建堆操作,并使得堆有序

        for($k=floor($len/2); $k>=0; $k--){

            sink($arr, $k, $idx);

        }

        //排序操作

        while($idx>0){

            //获取最大值操作

            list($arr[0], $arr[$idx]) = [$arr[$idx], $arr[0]];

            $idx--;

            //堆调整下沉

            sink($arr, 0, $idx);       

        }

    }

    //堆调整下沉,小数字下沉

    function sink(array &$arr, $low, $high){

        //从$low开始找子节点

        while($low*2+1<=$high){

            //获取low的直接左子节点

            $j = $low*2+1;

            //判断$low位置节点子节点中的较大子节点

            if($j<$high && $arr[$j]<$arr[$j+1]){

                $j++;

            }

            if($arr[$low]>$arr[$j]){

                break;

            }

            //交换low节点和子节点的值

            list($arr[$low], $arr[$j]) = [$arr[$j], $arr[$low]];

            //继续排查下沉后子节点是否需要进行下沉操作

            $low = $j;

        }

    }

    $a = [4,3,6,7,5,1,9,0,2];

    hsort($a, count($a));

    print_r($a);

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值