数学建模社区-数学中国

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

作者: 杨利霞    时间: 2020-4-23 15:00
标题: 十大经典排序算法之堆排序(Java语言)

9 i( y& K4 d1 y* r十大经典排序算法之堆排序(Java语言)
) H1 R( ^# S' X7 R文章目录
* i2 |/ \7 C  J' g
$ U; P; ?# ~* c8 f) K  [. D) \什么是堆
3 e' Z, q7 N, o8 f5 d+ y! [- d+ x3 i如何进行堆排序呢9 \$ H2 W" h: t
用数组构建一个堆0 z/ C4 z- `7 u* X. @  O3 u
上代码( i- j6 r. J( h* d- Z+ ]! {( e
什么是堆
, ~2 K, J1 I5 O" V, y& [. o% ^5 R8 q
在了解什么是堆之前一定要先了解什么是完全二叉树* v; }% g( t& _' j7 y! F0 M! c7 a" M2 q
看一下百度百科的介绍
+ C# K( _0 Y) Q1 k1 S( ]; G3 g& ~) C. ~( Z$ s, V
若设二叉树的深度为h,除第 h 层外,其它各层 (1~h-1) 的结点数都达到最大个数,第 h 层所有的结点都连续集中在最左边,这就是完全二叉树。
$ u- m$ T) A' F" M百度百科拗口版性质介绍,能看懂上面的就行,下面的大概看下
5 {% t! {5 m# o& h( w
- A* |% X, o7 k6 g$ z! S完全二叉树是由满二叉树而引出来的。对于深度为K的,有n个结点的二叉树,当且仅当其每一个结点都与深度为K的满二叉树中编号从1至n的结点一一对应时称之为完全二叉树。/ P4 q2 b  H5 H, I2 x5 U  O
(1)所有的叶结点都出现在第k层或k-l层(层次最大的两层)
& P& A8 j! b1 x! s6 |4 D; f2 V(2)对任一结点,如果其右子树的最大层次为L,则其左子树的最大层次为L或L+l。3 E! j) t0 P" W
一棵二叉树至多只有最下面的两层上的结点的度数可以小于2,并且最下层上的结点都集中在该层最左边的若干位置上,则此二叉树成为完全二叉树,并且最下层上的结点都集中在该层最左边的若干位置上,而在最后一层上,右边的若干结点缺失的二叉树,则此二叉树成为完全二叉树。
6 z! _) @1 V& X- \/ a; x那么在了解到什么是完全二叉树之后,我们再来看什么是堆
- }" _7 |- O. |; v7 _堆有以下两个性质# M9 J$ U0 J0 O4 Q* I3 ?4 X

, h; P: U5 z6 c* C1 堆中某个节点的值总是不大于或不小于其父节点的值;
) J  t: n& Y2 ]2 堆总是一棵完全二叉树。
& t4 R' K- H- \& u其中堆顶就对应二叉树的根5 N; p% e0 Z' d0 y

% s5 g8 C0 W4 ~: z+ s0 V% u堆又分为大顶堆和小顶堆,根据堆的第一个性质来进行区分
5 v$ |% D  q' j6 R; e9 ]
9 v2 Y5 V0 g4 o1 ?: m2 z) z当堆中某个节点的值总是大于它的子节点的时候这个堆为大顶堆,反之为小顶堆' A& e5 z) E( b5 l
如何进行堆排序呢- n9 d) r) e) p9 c/ E8 ~
. W/ u8 E: R  s/ R* N3 `- O
堆排序,其实就是每次构造出一个大顶堆或者小顶堆,然后取出堆顶的值,再将剩下的值重新构造成大顶堆或者小顶堆,最终到堆里的值全部取出来,取出来的数就是排好序的5 ~- W, @- _! j
4 q0 R  A( n' U; S4 O& |6 B& _9 r
用数组构建一个堆
( b/ f1 U% u0 r( d* O
/ b5 z% M$ {1 w5 K+ {因为堆是一颗完全二叉树,所以我们可以用数组来对其进行存储
+ L$ f6 l) ~: o对于用数组存储的二叉树,我们可以用如下方法来定义:
  ^3 P- s, W( ?" J0 s7 E假设当前节点的下标为 n
6 o! k! {; K0 ]
" k0 J. b. Y) j1、那么他的左子节点的下标 2*n + 1% j6 b* N$ L5 v( {
2、那么他的右子节点的下标 2*n + 2, `) q( `# U1 ~3 B0 `% B. D# T
3、他左边的节点是 n-1,如果当前节点是第 h 层的最左节点,那么第h-1层的最右节点的下标就是 n-1
, M  o' d  k, @0 j3 u% b& k/ }( R4、根据1、2可以推出来n节点的父节点是 (n-1)/2,不管当前节点是父节点的左子节点还是右子节点,都用 (n-1)/2就行了,因为整型数字相除小数点后面的会被截断& l+ z9 ~+ x/ d- U
那么有了上面四条性质,我们就可以开始动手了9 c1 \0 U& Q7 q: c5 d$ P
3 {, A2 `9 |7 c' {
1、假设我们要构建的堆是大顶堆,那么根据大顶堆的性质,任意节点都比它的左右子节点要大,所以我们肯定有个heapify方法,该方法调整指定节点和其子节点的位置,并且继续调整被调整的子节点和孙子节点的关系,直到没有调整或者到数的最底层1 A2 c# D4 F* p& ]
2、然后我们要有构造大顶堆的方法,构造大顶堆就是从最后一个节点的父节点开始调整,接设最后一个节点的父节点是n,那么我们就将n,n-1,n-2 ··· ··· 0,这些节点逐次,从大到小调用heapify方法,这些节点都调整完成后,大顶堆就构造完成了' H/ g9 X7 M* M: q8 }
3、接下来就开始将堆顶和堆尾互换,并砍断堆尾的操作了,由于互换之前,这是一个符合条件的大顶堆,但是换完只有只有一个堆顶这里不满足了,那么我们重新调整一下堆顶的三个元素就可以,还是调用heapify方法,这个方法会自上而下的重新调整堆,使其成为一个大顶堆; N0 r3 Q5 O8 L

! k0 k+ d  j6 Z; B# L/ c; D堆排序的性质
" l1 X# Q% t+ v9 _9 @& A( `
8 j) \1 b, V% g: R4 A- l2 }中文名称        英文名称        平均时间复杂度        最坏时间复杂度        最好时间复杂度        空间复杂度        稳定性
: {& f1 F" Z% n6 A堆排序        Heap        n*logn        n*logn        n*logn        1        不稳定
" R  _% T( {" G4 l上代码
/ v* x5 `. w1 Y; H  m5 ~6 B
% }8 Z* x# T5 l/**
3 Z2 f+ X" K; Y. d* w9 T$ W# r * 交换第n和m个元素8 R8 W; A3 N% K; ^
*/, ~" e& K0 ^; L( V- u
private static void swap(int arr[], int n, int m){- N' H6 t  b+ T: r  D$ y) t+ m7 ^
    int temp = arr[n];3 i) l( O1 }: d, l! A/ R1 `
    arr[n] = arr[m];7 f5 e3 D( a7 {/ x
    arr[m] = temp;& ]" ], j; ?0 U. H" ^
}
4 N1 t( \* h# V4 M, ?
/ ?5 ~* @9 H% z/**9 [: T6 {2 ]9 E2 n
* 调整指定节点和其子节点
- d) ]: c3 s: N* l, I- X  s * @param tree 整棵树& [$ D9 b8 o6 i
* @param n 数组长度,树的元素个数
8 o0 w; {8 R+ K4 ^% a6 B) d' v/ D5 u * @param i 要调整的节点的下标- e2 E8 z0 h( P$ h7 y
*/" `, _6 C2 Z. m. V. R" L
private static void heapIfy(int tree[], int n, int i){
- S4 @: U1 w# ~' `$ M( j    if(i >= n){
+ F/ ^/ I8 E) S- h. G. E3 R        return;9 @1 t: Q: \8 I
    }
6 m2 g% n: G$ v& w7 E( P    int c1 = 2 * i + 1;//左子节点的下标
1 T- x) u3 @1 ]  V" j" a    int c2 = 2 * i + 2;//右子节点的下标( V  m7 J: F' Y- ?* V1 W
    int max = i;//假设父节点是最大的
1 D/ L& S7 R' b  K* l    //找出最大值的下下标
$ U: p" O0 C, c$ n6 A9 v    if(c1 < n && tree[c1] > tree[max]){
) s2 q7 B  u* a: J; f. V        max = c1;5 h% A* h2 z) X, ^& E' v. w
    }/ }/ N: k" d3 w0 L; e' U
    if(c2 < n && tree[c2] > tree[max]){6 t* J0 H2 i/ o
        max = c2;
, j: o' @9 ]/ z) n    }6 h" l7 a# k: I
    if(max != i){//如果最大值不是父节点,需要做换位置操作
: |' P, a" j) t+ @1 S" X5 t( e        swap(tree, max, i);. @: V7 r, x9 Z4 X9 L& u: m
        //此时,i节点被换成最大值了,符合大顶堆的性质, c0 Y5 b1 [. U4 u
        //但是换到下面的节点不能保证比他的两个子节点都要大
+ L7 I  }/ x* [" A" N- G. X        //所以被换位置的节点继续调整& \1 H% a8 B! R, A  B
        heapIfy(tree, n, max);
( h4 ~. P/ e# U) B- |- w+ y1 q    }
6 p1 f3 d3 Y9 m: b% H( |  C$ v9 ~}$ j. s6 c9 \2 ^

1 l3 ?) ^9 |0 C* u7 s/ |! k. s/ b0 N/**4 U: Z( c' @( Y# l8 s1 Q- P
* 完整构建大顶堆6 }/ ^# m  @$ U& p
* @param arr 用于构建堆的数组
4 G: Q$ d& h0 G" h1 ^ * @param n 堆的最后一个节点的下标) T  u8 y0 q2 D) I% [
*/
( M1 z8 W# E! ?8 G' C( wprivate static void buildHeap(int arr[],int n){
/ J  I! F  k9 C    int lastNode = n - 1;' ~) Q( g- b( [3 M+ X
    int parent = (lastNode - 1) / 2;/ c2 w' e( m' ~& s) W8 T' R" g1 X
    for (int i = parent; i >= 0; i--){" L+ r4 U. J4 g, f, B
        heapIfy(arr, n, i);
7 v# `3 b6 w% c/ V! U    }$ K; J9 ~. l/ y
}% f3 R5 X# c( [  L9 Z

! Q- C# j6 W' Y4 x/**$ F2 k9 _2 b5 v3 S
* 堆排序
4 D1 q; u8 E( W- z, ^ * @param arr 待排数组
3 D$ H+ {/ ]" U2 B# k8 c+ |+ A  i */  j8 d4 x5 z" I3 S
public static void sort(int arr[]){
8 @8 Q6 \# u1 ?8 w    buildHeap(arr, arr.length);//先构造大顶堆5 [2 R; i* s% [' d% Z4 O( y  |
    //每次构建堆后将根节点和最后一个节点进行交换
$ h  k+ O0 B. z. o6 V1 N; s    //然后砍断最后一个节点
& p% H# ?: k' A    //所以从最后一个节点向前循环; `; r4 g* B9 o) i' n
    for (int i = arr.length - 1; i > 0; i--){" g7 G  I3 s5 M! t, j* }+ C4 h( I
        swap(arr, 0, i);
& d( @. v7 w6 U6 q, l/ ]7 Y' q        heapIfy(arr, i, 0);( @2 t- J* m/ h/ H5 n9 t& @
    }
3 z5 \% U2 D+ J/ `}- l5 ~/ S  o. T: l: B/ U
————————————————
7 Q5 E5 n" d2 C) m; u. M版权声明:本文为CSDN博主「qq_34912889」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。: r4 o, ^& F* J9 e+ H1 }
原文链接:https://blog.csdn.net/qq_34912889/article/details/105690644
) l* A5 K7 S# B7 j: v; }2 T4 i4 k8 C+ y6 r: m& l: `  i  X
6 j& a- n+ f  I) \9 e





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