数学建模社区-数学中国

标题: 十大经典排序算法之堆排序(Java语言) [打印本页]

作者: 杨利霞    时间: 2020-4-23 15:00
标题: 十大经典排序算法之堆排序(Java语言)
. a+ X: K8 W% u; P! K) c1 H
十大经典排序算法之堆排序(Java语言)( _! M4 ~0 X3 c2 q3 m4 [3 c
文章目录) U1 F/ n7 f- a

4 U* C1 z4 ?+ A. y什么是堆! Z0 Q. C9 I4 ?9 Z
如何进行堆排序呢8 y8 Y' B9 q3 y5 O3 v
用数组构建一个堆5 |8 Q% o/ @) H2 R
上代码
! @2 U1 X% e0 ^) a什么是堆
* D6 `8 x+ d; L5 t/ M2 }( b9 w8 a6 H: C! D9 b' U4 l
在了解什么是堆之前一定要先了解什么是完全二叉树
% I+ o3 Q* M* c0 v3 s2 S, M4 v3 D看一下百度百科的介绍0 L6 e! ?; E2 k$ [
$ ?9 ^. ?9 q! w3 U6 ^% M$ _
若设二叉树的深度为h,除第 h 层外,其它各层 (1~h-1) 的结点数都达到最大个数,第 h 层所有的结点都连续集中在最左边,这就是完全二叉树。
4 o7 ~. C2 I( U百度百科拗口版性质介绍,能看懂上面的就行,下面的大概看下
; S1 o" L3 Z' g0 L
+ o) {6 v5 u  e! O- i完全二叉树是由满二叉树而引出来的。对于深度为K的,有n个结点的二叉树,当且仅当其每一个结点都与深度为K的满二叉树中编号从1至n的结点一一对应时称之为完全二叉树。
, C& G& {5 M3 B' S(1)所有的叶结点都出现在第k层或k-l层(层次最大的两层)
* Z( Z  }* _9 S4 a% @5 y(2)对任一结点,如果其右子树的最大层次为L,则其左子树的最大层次为L或L+l。
$ N4 G! m5 Q+ R4 x' K; s0 G2 }3 d一棵二叉树至多只有最下面的两层上的结点的度数可以小于2,并且最下层上的结点都集中在该层最左边的若干位置上,则此二叉树成为完全二叉树,并且最下层上的结点都集中在该层最左边的若干位置上,而在最后一层上,右边的若干结点缺失的二叉树,则此二叉树成为完全二叉树。
! @7 k/ j- X: Q% O) W那么在了解到什么是完全二叉树之后,我们再来看什么是堆
) v4 \8 n$ }$ I0 a( U  F7 V堆有以下两个性质
) \2 n, J& A: s' @! K, [) X
+ C* q9 V8 {2 M- J7 k* }1 堆中某个节点的值总是不大于或不小于其父节点的值;9 s8 k8 r5 I& c
2 堆总是一棵完全二叉树。# |+ p2 w) ?* n: b  G3 f7 r2 A: q& |
其中堆顶就对应二叉树的根: [: S! z0 p! D' r- o& O6 j  V; ?
. U, p+ u+ E( u& N
堆又分为大顶堆和小顶堆,根据堆的第一个性质来进行区分$ {; l6 B0 M* I; a( V) M
. E9 z: J: h3 Z+ X" `3 C
当堆中某个节点的值总是大于它的子节点的时候这个堆为大顶堆,反之为小顶堆
' d8 v+ R* T* ?+ I如何进行堆排序呢+ F9 n. E" d8 `+ ~% K# F
9 T8 B7 }* [0 E+ U% w  w0 m' Q
堆排序,其实就是每次构造出一个大顶堆或者小顶堆,然后取出堆顶的值,再将剩下的值重新构造成大顶堆或者小顶堆,最终到堆里的值全部取出来,取出来的数就是排好序的
9 I& r- m3 B+ b8 \$ a' F
8 x& q$ |7 W6 K5 N' M8 S, ^3 `用数组构建一个堆
  N' l4 h: G! \3 j8 Z# N1 c) b
因为堆是一颗完全二叉树,所以我们可以用数组来对其进行存储
& C, V' k5 u# I* J' v; H对于用数组存储的二叉树,我们可以用如下方法来定义:
9 z, ]) o( ?5 r4 i假设当前节点的下标为 n
0 `4 x! q7 Q% o; f2 o
' _0 n3 U( h$ p, y1、那么他的左子节点的下标 2*n + 1, ]. `4 C1 g. ]! Y
2、那么他的右子节点的下标 2*n + 2
. t( @9 h0 H! B  t8 [! ^4 ~3、他左边的节点是 n-1,如果当前节点是第 h 层的最左节点,那么第h-1层的最右节点的下标就是 n-1# `2 H+ }% h7 L# g- t
4、根据1、2可以推出来n节点的父节点是 (n-1)/2,不管当前节点是父节点的左子节点还是右子节点,都用 (n-1)/2就行了,因为整型数字相除小数点后面的会被截断% S, E* a+ f5 F0 d% ?
那么有了上面四条性质,我们就可以开始动手了
) `+ b/ ~5 @. `# \- ]5 o1 e2 o! v- o1 ^+ k1 n4 ?+ P
1、假设我们要构建的堆是大顶堆,那么根据大顶堆的性质,任意节点都比它的左右子节点要大,所以我们肯定有个heapify方法,该方法调整指定节点和其子节点的位置,并且继续调整被调整的子节点和孙子节点的关系,直到没有调整或者到数的最底层* @( p3 e  H2 _9 \
2、然后我们要有构造大顶堆的方法,构造大顶堆就是从最后一个节点的父节点开始调整,接设最后一个节点的父节点是n,那么我们就将n,n-1,n-2 ··· ··· 0,这些节点逐次,从大到小调用heapify方法,这些节点都调整完成后,大顶堆就构造完成了/ {& X' D* G  X4 \# i
3、接下来就开始将堆顶和堆尾互换,并砍断堆尾的操作了,由于互换之前,这是一个符合条件的大顶堆,但是换完只有只有一个堆顶这里不满足了,那么我们重新调整一下堆顶的三个元素就可以,还是调用heapify方法,这个方法会自上而下的重新调整堆,使其成为一个大顶堆
9 V& ?+ |4 m# C1 T. z1 J6 ]$ u# l1 T8 a4 l4 y+ B# R$ B" Y
堆排序的性质2 u, e/ C" ]9 j& a% l8 X0 [

2 [! p- H8 k! u9 H中文名称        英文名称        平均时间复杂度        最坏时间复杂度        最好时间复杂度        空间复杂度        稳定性
% L2 ]. [) g" j堆排序        Heap        n*logn        n*logn        n*logn        1        不稳定5 Y$ x8 E4 h; _2 S
上代码0 i! Q$ P( }" c7 g6 R5 l

1 }4 b5 Z+ g' s/**% S& {) I. y+ [6 R  l
* 交换第n和m个元素
3 r% e/ u% p! ~4 Z6 q1 Q */
) T3 s* P# W( R8 E8 A0 w1 gprivate static void swap(int arr[], int n, int m){
) J2 a9 T$ k$ o! u% b: ~    int temp = arr[n];5 X; Q9 F/ n/ \
    arr[n] = arr[m];8 {% x( C7 R7 G# n* E
    arr[m] = temp;. L) L3 Z" l/ Z
}
. e  y! Z7 k" v. D- N; U1 X4 W7 e6 i
/**
- n- G/ p# l- p. t. n" c+ K * 调整指定节点和其子节点: z3 z& a2 J+ d7 T7 e! T
* @param tree 整棵树' S, I! V! u. y2 Z
* @param n 数组长度,树的元素个数" C( D" ~, r/ r7 }: Q
* @param i 要调整的节点的下标
7 W; z5 O9 o1 q) A */
# ?& H; [5 U* T6 Q! [& B2 fprivate static void heapIfy(int tree[], int n, int i){
! ]; n" C, ^- t+ s    if(i >= n){
( B. d, q9 C: s2 _0 |6 F- L' E        return;
. B8 g- o; k+ x5 {  v4 c+ `7 a    }( @! P1 p7 G3 o( I. c
    int c1 = 2 * i + 1;//左子节点的下标
5 `9 @+ P2 l4 V    int c2 = 2 * i + 2;//右子节点的下标
( O+ H% J2 e( W9 {# ~% O4 k- q    int max = i;//假设父节点是最大的
9 F$ P3 b" k4 z) C3 l& l    //找出最大值的下下标
/ h# z  l0 c, J: x8 E# `    if(c1 < n && tree[c1] > tree[max]){1 `7 C9 w( a( P) f7 N) L
        max = c1;
# G: X! V- U8 e0 ~- {    }- p% p7 r4 ?/ `1 K( \8 Q
    if(c2 < n && tree[c2] > tree[max]){* W9 T7 K4 R6 h* `# n7 [
        max = c2;
* I3 v- [5 q. A5 }1 i    }
7 I+ u: `3 w  H& d1 y& K6 g    if(max != i){//如果最大值不是父节点,需要做换位置操作9 C' E0 ~# ~+ P" I
        swap(tree, max, i);
0 ^0 _8 i6 y) {4 V8 _! X        //此时,i节点被换成最大值了,符合大顶堆的性质
5 g" p; Q2 b* y  y1 J/ @9 t        //但是换到下面的节点不能保证比他的两个子节点都要大+ r. ]- i* G7 I+ T
        //所以被换位置的节点继续调整
) `% y6 V& f& U8 A+ G  `        heapIfy(tree, n, max);4 d' R2 V& o7 ?9 z- f. e' D
    }1 d0 a# d8 [0 b2 ^$ J. V
}( Z# `* W: |# R/ z
- z$ Z4 q6 y9 f- ^  j2 r
/**" E; E* O5 F' E: j4 d9 Y/ E$ D
* 完整构建大顶堆6 x( e' o0 t- z0 }) D  w* A2 S
* @param arr 用于构建堆的数组
' j. }. {0 _2 Y) F3 M * @param n 堆的最后一个节点的下标4 G' c7 D; R9 ~& @
*/
! M1 U1 T  ?8 v$ p7 ^# }4 Cprivate static void buildHeap(int arr[],int n){* g" I( y7 C# S( }# Y- }" g) y
    int lastNode = n - 1;5 V9 `, ]7 c+ ?; E7 d
    int parent = (lastNode - 1) / 2;( g/ L+ d. I* G: D/ [
    for (int i = parent; i >= 0; i--){9 A" Q' k3 ~# B' i$ }
        heapIfy(arr, n, i);  J& _+ A- u6 k
    }
( Z, F4 ?/ J: U  ^5 o}( @5 b- k2 X3 z( W- S% y

, _" {& `1 M# I' y9 `/**
% \2 G, c+ |8 a( d2 s0 D2 x0 s * 堆排序; `: S3 n9 O7 D
* @param arr 待排数组$ W9 w( X+ u# v; y" l
*/
+ w, C; ^* s( j6 h5 X4 apublic static void sort(int arr[]){* @: \6 Z- q4 U% c: }
    buildHeap(arr, arr.length);//先构造大顶堆
# O$ i( t: W% i) y2 O; V+ y    //每次构建堆后将根节点和最后一个节点进行交换& q( R; |# D1 J# f! o: k! M1 D9 K
    //然后砍断最后一个节点
0 @. W3 G% r: D& ?    //所以从最后一个节点向前循环5 s) l& Z' R- q9 j& {& N
    for (int i = arr.length - 1; i > 0; i--){
4 X$ e* t9 }! Y2 `: R( w8 T        swap(arr, 0, i);: V8 w7 w2 \7 }2 }& t* r6 x3 _$ o
        heapIfy(arr, i, 0);! `$ o9 _. }  g
    }
8 H! z) ^% O" U. {4 [! _+ B0 D}: p5 v1 d; _' b, k% i4 B
————————————————0 }8 Z4 J$ y  `' F9 _2 q0 M
版权声明:本文为CSDN博主「qq_34912889」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
( t4 ~; \/ x7 k1 t0 Y  B" L原文链接:https://blog.csdn.net/qq_34912889/article/details/105690644
, p$ N, b% g  E) b2 ]: u: K' L
; c5 G* S& {6 [( K- u6 i+ a
, l$ R/ z* U& d7 H% l




欢迎光临 数学建模社区-数学中国 (http://www.madio.net/) Powered by Discuz! X2.5