数学建模社区-数学中国

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

作者: 杨利霞    时间: 2020-4-23 15:00
标题: 十大经典排序算法之堆排序(Java语言)
* D8 q, Z7 o6 h- D
十大经典排序算法之堆排序(Java语言)/ B, @* m( x, y2 l, p1 B
文章目录; _; m* n8 a. ?  S" X) ^) \! l2 R
  U; |2 a% J1 L1 q1 |9 H+ ]2 ?
什么是堆
9 [4 O4 g- z3 [7 h& w- _* t如何进行堆排序呢0 c( I6 t" b% i- c, x
用数组构建一个堆$ g( m4 z8 t  F. q
上代码
: a$ b' R/ Y* h8 O什么是堆
. L3 N# F/ W9 T/ L& J  v0 ~9 M9 C+ n# c& c% p
在了解什么是堆之前一定要先了解什么是完全二叉树
4 ~+ d7 N+ \" c+ l, S% Y看一下百度百科的介绍
/ C# R' J- a, g8 y( I6 \* q4 d) _" q% `8 i, R$ |/ J
若设二叉树的深度为h,除第 h 层外,其它各层 (1~h-1) 的结点数都达到最大个数,第 h 层所有的结点都连续集中在最左边,这就是完全二叉树。
1 ]* C$ q% U8 e4 _; Y& D- C百度百科拗口版性质介绍,能看懂上面的就行,下面的大概看下
# T  y+ S' b3 D# R7 w2 ]6 w* p* M4 w$ y( T6 Q, a4 i
完全二叉树是由满二叉树而引出来的。对于深度为K的,有n个结点的二叉树,当且仅当其每一个结点都与深度为K的满二叉树中编号从1至n的结点一一对应时称之为完全二叉树。+ M' u; a( w: A$ y3 T% D
(1)所有的叶结点都出现在第k层或k-l层(层次最大的两层)" Y, u  S# \  }& U5 p& `. l
(2)对任一结点,如果其右子树的最大层次为L,则其左子树的最大层次为L或L+l。% S- n3 p2 z" D3 g4 g
一棵二叉树至多只有最下面的两层上的结点的度数可以小于2,并且最下层上的结点都集中在该层最左边的若干位置上,则此二叉树成为完全二叉树,并且最下层上的结点都集中在该层最左边的若干位置上,而在最后一层上,右边的若干结点缺失的二叉树,则此二叉树成为完全二叉树。* Q1 B" S4 m5 S' Y" V
那么在了解到什么是完全二叉树之后,我们再来看什么是堆' F' z3 y, f' e
堆有以下两个性质4 U2 ]! @6 m! p: T
) o) }( D% i7 h
1 堆中某个节点的值总是不大于或不小于其父节点的值;+ g% Z* k1 T: q9 p
2 堆总是一棵完全二叉树。
, {. ~' m1 J+ d$ j. }8 f其中堆顶就对应二叉树的根$ n5 W* f' X  f% ?
4 r  \9 _# J* w% @/ ^2 g1 e- \" Z
堆又分为大顶堆和小顶堆,根据堆的第一个性质来进行区分
) \% {% m3 r4 \, ~. Q  f  J# D; Z( _. R' e8 a
当堆中某个节点的值总是大于它的子节点的时候这个堆为大顶堆,反之为小顶堆
) f: Q7 y5 f: H8 `7 D" K# ]如何进行堆排序呢
# a! t% {( `& I8 S4 y0 C4 q/ i& ^) g' m$ V7 `
堆排序,其实就是每次构造出一个大顶堆或者小顶堆,然后取出堆顶的值,再将剩下的值重新构造成大顶堆或者小顶堆,最终到堆里的值全部取出来,取出来的数就是排好序的4 M- E3 g4 R" h! f
/ w6 o6 }) e/ I) Y  g
用数组构建一个堆
$ r4 s( K5 [- t  H  q7 Y
7 ?: Q. R" x8 ~% y因为堆是一颗完全二叉树,所以我们可以用数组来对其进行存储8 a% d- o; O8 \" j( }- i# @' ?# s! j
对于用数组存储的二叉树,我们可以用如下方法来定义:
0 j, U; ?7 c9 z2 _5 o! B! `, d假设当前节点的下标为 n
' C" o7 R, o& W9 L; L2 E2 v% j% q# Z- Q# z+ J
1、那么他的左子节点的下标 2*n + 1
: C' n: g* ?: ^4 x3 @5 p2、那么他的右子节点的下标 2*n + 2( o3 E! I1 a+ n7 h) f' G
3、他左边的节点是 n-1,如果当前节点是第 h 层的最左节点,那么第h-1层的最右节点的下标就是 n-1
$ Z' i0 X7 W( h4、根据1、2可以推出来n节点的父节点是 (n-1)/2,不管当前节点是父节点的左子节点还是右子节点,都用 (n-1)/2就行了,因为整型数字相除小数点后面的会被截断
; d' i6 H6 x% g9 D/ K那么有了上面四条性质,我们就可以开始动手了
1 x- \8 |: ~! _. }! i: L2 `
& ]( K& t9 |- P. |1、假设我们要构建的堆是大顶堆,那么根据大顶堆的性质,任意节点都比它的左右子节点要大,所以我们肯定有个heapify方法,该方法调整指定节点和其子节点的位置,并且继续调整被调整的子节点和孙子节点的关系,直到没有调整或者到数的最底层
- [1 E7 t  w' ?0 P$ |: P2、然后我们要有构造大顶堆的方法,构造大顶堆就是从最后一个节点的父节点开始调整,接设最后一个节点的父节点是n,那么我们就将n,n-1,n-2 ··· ··· 0,这些节点逐次,从大到小调用heapify方法,这些节点都调整完成后,大顶堆就构造完成了
( r! \; _8 X: f$ F: ]3 R0 Y7 q# w3、接下来就开始将堆顶和堆尾互换,并砍断堆尾的操作了,由于互换之前,这是一个符合条件的大顶堆,但是换完只有只有一个堆顶这里不满足了,那么我们重新调整一下堆顶的三个元素就可以,还是调用heapify方法,这个方法会自上而下的重新调整堆,使其成为一个大顶堆
; b6 j6 p8 m4 n4 J8 I6 J2 d; K* c" v) ~4 M) P6 u
堆排序的性质
7 [  ~' K4 b- ?" ^& x5 P9 [
, {+ \- ?1 i% D/ b" g  Y/ x  R2 E中文名称        英文名称        平均时间复杂度        最坏时间复杂度        最好时间复杂度        空间复杂度        稳定性
( a  L/ p7 j$ \- U堆排序        Heap        n*logn        n*logn        n*logn        1        不稳定( N: t% g( _7 K5 F; T2 D( o
上代码" `0 @, \- i/ Q
$ o" v/ C. B" @
/**
9 m# c, c4 o7 H3 G6 ^' F1 }" ]9 w * 交换第n和m个元素1 `( z* g$ f( K5 n" d+ j4 n
*/  n. @( s# f; f& a# v, N/ q
private static void swap(int arr[], int n, int m){% e9 y8 Z5 _- [! z. ^
    int temp = arr[n];6 Z' h- Y8 S2 G" p# j, p
    arr[n] = arr[m];* y6 ^8 M. A: c. U+ G; q5 r. G* f
    arr[m] = temp;
# X( T5 E: [: `3 C}- m2 ]% l! x9 V" }

% G( P, `- Q8 B- i$ @3 _3 _/**0 l' j8 ?4 l, a4 Q
* 调整指定节点和其子节点
8 V- J1 m* o( \$ B' G: s) \  u * @param tree 整棵树
; z, Y5 _& W) l  c, u/ v- O9 X * @param n 数组长度,树的元素个数% U2 W7 _& a, `. H2 F  F2 d
* @param i 要调整的节点的下标4 `+ R8 m- x6 \" K. \. g0 W- o3 z
*/
, O) \, |: H+ X" X5 dprivate static void heapIfy(int tree[], int n, int i){) c: R" {) E8 U! I) q- B# \
    if(i >= n){
: r2 J$ l2 @1 H7 l        return;. W* H( F3 a9 ]% t6 H
    }- e4 N% ?( d. L6 p; e
    int c1 = 2 * i + 1;//左子节点的下标) `% ?# I& K( Q7 N7 r
    int c2 = 2 * i + 2;//右子节点的下标: ]! ^3 h3 g. j0 Z. X; s& T+ f
    int max = i;//假设父节点是最大的& I  ]( r4 Z; T" A5 f5 Z
    //找出最大值的下下标
- I1 G( G" F. h    if(c1 < n && tree[c1] > tree[max]){  U, I" H1 |3 `# E! L
        max = c1;: Y1 o# ]7 q. {- }) a* h
    }+ P$ Y6 D# v) d  e" _% W7 O
    if(c2 < n && tree[c2] > tree[max]){
; _; l. H. q; Y- J$ _3 c) ]        max = c2;% R9 W% j) H4 w* g, l
    }5 C  s( S! Q7 x+ o% r
    if(max != i){//如果最大值不是父节点,需要做换位置操作
( L! V' Q3 B- y/ A        swap(tree, max, i);3 ~: w7 K4 i: o, a* b% o9 M8 T
        //此时,i节点被换成最大值了,符合大顶堆的性质* U0 A1 W5 h( i* K3 \
        //但是换到下面的节点不能保证比他的两个子节点都要大3 h- o, C4 T% n2 k: e8 }* C5 `; K
        //所以被换位置的节点继续调整: w- y7 ]1 Y) c$ q& Q" x- |) j
        heapIfy(tree, n, max);
: Y: }) B4 J' o7 `    }/ n3 M) `2 V, _+ j) }
}
5 Y0 K2 a) O& |4 l3 [5 M$ n
2 A4 }* @0 C% Q& x/ H/**; j  r# P  f" V& A
* 完整构建大顶堆" V4 C! @  i8 x, A" ~" p, j
* @param arr 用于构建堆的数组
6 l8 o9 q( B$ J  v * @param n 堆的最后一个节点的下标
& H8 L  N% f  N5 X$ x */
; b. r3 D. L- e- E7 fprivate static void buildHeap(int arr[],int n){
" r( y2 p) J$ T    int lastNode = n - 1;* n1 J( e4 a: R% `, y. t
    int parent = (lastNode - 1) / 2;' f) o# [5 H/ ?5 P, J
    for (int i = parent; i >= 0; i--){
0 V9 d" z! C: \2 Z$ ~0 w        heapIfy(arr, n, i);
7 w) D! h2 ~) T- W9 d    }/ A! D1 R. S2 ?: ]9 w
}* l) j) s- u# y
. ^- ]8 s% }8 V4 e, T! X* q! b' R' m3 w9 t
/**
/ N# I2 t! a; `5 O5 F3 O" T * 堆排序
9 J/ |2 x0 _7 P * @param arr 待排数组
% t, F- Q! d- k */
) ^! R1 L0 a4 y( q+ jpublic static void sort(int arr[]){
8 ]( F+ g' |' H* E  O    buildHeap(arr, arr.length);//先构造大顶堆* e. b7 y* z! f. M
    //每次构建堆后将根节点和最后一个节点进行交换6 B7 t0 J8 h* `; _
    //然后砍断最后一个节点
2 l2 p. ^6 i9 }! W& r    //所以从最后一个节点向前循环
& A6 M1 W0 o" x  q: F! U' h    for (int i = arr.length - 1; i > 0; i--){
) y0 e% m2 y8 V; {& n% y        swap(arr, 0, i);* B% g# y. Z% q" y& H
        heapIfy(arr, i, 0);
/ b; @% f! `/ U2 z' z. H    }
2 C  z2 h3 J( V}
* |) j* x; Q4 v————————————————  |$ E1 j( I, a5 A  {5 M
版权声明:本文为CSDN博主「qq_34912889」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
; K' F2 a- H  R6 V% q$ n$ Y9 h$ J原文链接:https://blog.csdn.net/qq_34912889/article/details/105690644
+ j8 |( G8 q* r; A5 l; h- G% i
/ D( Y( \3 b3 m$ w) U% i( a! v) S2 E& v/ q! O8 Z





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