数学建模社区-数学中国

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

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

( W% O. q' H$ x9 m# Q十大经典排序算法之堆排序(Java语言)* f, l1 y( U. T- ~3 i0 v2 o
文章目录# R' e5 U+ R% z' b" e
- m( X* s% V" p/ k7 r- |; j
什么是堆1 }" J( t# W& Z; P
如何进行堆排序呢
/ l# Y' x0 T# r5 ^; R7 }1 n" p6 C用数组构建一个堆) q$ \6 `0 ?5 {! F/ x" a
上代码$ `6 k# ?2 H4 ?8 H; ], q% p- \( K) I
什么是堆& K+ l& _7 m- U3 s! y& e+ d
, ?% K5 h# T8 D' _
在了解什么是堆之前一定要先了解什么是完全二叉树
- _5 j8 V( r+ R7 b看一下百度百科的介绍
8 O5 L: T/ E8 o  J* J4 _& T/ s6 q. {8 j) |; s- d9 s, a8 d
若设二叉树的深度为h,除第 h 层外,其它各层 (1~h-1) 的结点数都达到最大个数,第 h 层所有的结点都连续集中在最左边,这就是完全二叉树。( J2 T! @- G& I) P& @; e
百度百科拗口版性质介绍,能看懂上面的就行,下面的大概看下6 R2 ]. C( e. T% m" b3 J- Y/ g

" X8 g9 d( b) l( s3 S( z& d完全二叉树是由满二叉树而引出来的。对于深度为K的,有n个结点的二叉树,当且仅当其每一个结点都与深度为K的满二叉树中编号从1至n的结点一一对应时称之为完全二叉树。
  g) L8 ?1 t( A, v9 x(1)所有的叶结点都出现在第k层或k-l层(层次最大的两层)( ]% z4 Z- `7 ?: j
(2)对任一结点,如果其右子树的最大层次为L,则其左子树的最大层次为L或L+l。
0 A2 V+ d* I5 M+ T. [  [一棵二叉树至多只有最下面的两层上的结点的度数可以小于2,并且最下层上的结点都集中在该层最左边的若干位置上,则此二叉树成为完全二叉树,并且最下层上的结点都集中在该层最左边的若干位置上,而在最后一层上,右边的若干结点缺失的二叉树,则此二叉树成为完全二叉树。
0 o; R. t6 d4 ?那么在了解到什么是完全二叉树之后,我们再来看什么是堆3 X" S( j% N+ w9 Q
堆有以下两个性质; s5 {5 _, c* C& G0 L

/ P: r- b  G+ a# A+ M1 堆中某个节点的值总是不大于或不小于其父节点的值;
6 M. g7 i; o8 I& D( z, R9 ?6 r2 堆总是一棵完全二叉树。9 h) t5 o7 N; B# B
其中堆顶就对应二叉树的根, n6 j+ G* l4 p; Y2 X5 d
* v6 ~9 E& V# o, v* w" G
堆又分为大顶堆和小顶堆,根据堆的第一个性质来进行区分3 n8 G2 }; [' I9 m# C+ D

$ E+ A8 e" B; B$ @) L) y/ Z8 m3 O当堆中某个节点的值总是大于它的子节点的时候这个堆为大顶堆,反之为小顶堆
3 p: ~* u  a1 x如何进行堆排序呢) a# ]) Q+ @: s
% n! G+ L: u/ t$ ]" g* X2 n' W
堆排序,其实就是每次构造出一个大顶堆或者小顶堆,然后取出堆顶的值,再将剩下的值重新构造成大顶堆或者小顶堆,最终到堆里的值全部取出来,取出来的数就是排好序的
/ o! s: V, ]/ _" p* u+ m. R) p, d8 Y) x! i5 l- x
用数组构建一个堆
8 [( L7 L' o) x8 l6 ?7 c
- y, K6 w! j' U' a+ @因为堆是一颗完全二叉树,所以我们可以用数组来对其进行存储
' Q/ I' ]' m3 p# V- `9 k: ~. u对于用数组存储的二叉树,我们可以用如下方法来定义:
) e. N7 i1 e" d# L) i假设当前节点的下标为 n& R  i) b, I6 C

+ h( B; c* U5 _# f% L1、那么他的左子节点的下标 2*n + 1
  }0 c2 z# t# @% P1 m) A, c2、那么他的右子节点的下标 2*n + 2
" @8 u& x. C6 z' I3、他左边的节点是 n-1,如果当前节点是第 h 层的最左节点,那么第h-1层的最右节点的下标就是 n-18 ]3 w. s( U. {5 L& _* {5 _4 ]
4、根据1、2可以推出来n节点的父节点是 (n-1)/2,不管当前节点是父节点的左子节点还是右子节点,都用 (n-1)/2就行了,因为整型数字相除小数点后面的会被截断; P& [2 N; ^7 j* T6 {6 O- N3 P( Q
那么有了上面四条性质,我们就可以开始动手了
  ?! ]- R  A  `6 ?* n& J7 y* l* E* X8 _7 P. `- u% }
1、假设我们要构建的堆是大顶堆,那么根据大顶堆的性质,任意节点都比它的左右子节点要大,所以我们肯定有个heapify方法,该方法调整指定节点和其子节点的位置,并且继续调整被调整的子节点和孙子节点的关系,直到没有调整或者到数的最底层
, ?9 b% X# Q3 M* h4 K2、然后我们要有构造大顶堆的方法,构造大顶堆就是从最后一个节点的父节点开始调整,接设最后一个节点的父节点是n,那么我们就将n,n-1,n-2 ··· ··· 0,这些节点逐次,从大到小调用heapify方法,这些节点都调整完成后,大顶堆就构造完成了
7 C/ F/ q' N" X# B5 f& _' c3、接下来就开始将堆顶和堆尾互换,并砍断堆尾的操作了,由于互换之前,这是一个符合条件的大顶堆,但是换完只有只有一个堆顶这里不满足了,那么我们重新调整一下堆顶的三个元素就可以,还是调用heapify方法,这个方法会自上而下的重新调整堆,使其成为一个大顶堆
' `( e6 A$ b2 Q: H% ]$ q% e& y" o2 Z, _
堆排序的性质
$ e: w+ c( j) R5 v* C. O' f9 g1 r7 _& U$ \. ]
中文名称        英文名称        平均时间复杂度        最坏时间复杂度        最好时间复杂度        空间复杂度        稳定性
4 S2 v% @6 S2 y; `! A3 a3 W% b堆排序        Heap        n*logn        n*logn        n*logn        1        不稳定
5 K( h0 F. c2 Z& j9 C- |1 j+ L8 \上代码
. u' D4 e4 G4 z# d9 {* w6 A8 B0 o1 F* \# G
/**- v# d' b4 b6 x$ x8 S4 H
* 交换第n和m个元素& Z* B0 \2 i6 g% e6 F5 P
*/5 f' F9 b- I7 e1 E& A8 G
private static void swap(int arr[], int n, int m){
$ {. v4 j' {3 _7 R: e    int temp = arr[n];! Z3 `+ @( t( U
    arr[n] = arr[m];
5 H( a* g& B0 ~7 Y8 U    arr[m] = temp;
4 A7 F- Y6 \+ Q  |" w% q}
* q- [3 a9 x) V- a, Y8 J& z  X
1 [7 f* d, @- A/**% a% `4 l4 n" f+ ]' A% |& X/ p
* 调整指定节点和其子节点
+ W  T" X4 B* P" {* k * @param tree 整棵树  P7 M! @6 C  u
* @param n 数组长度,树的元素个数
* \/ }# y( N4 Q/ I * @param i 要调整的节点的下标& j, A8 ~, ?: I. f+ G: X8 Y
*/
% c) {# T# a$ F& ?9 z8 Dprivate static void heapIfy(int tree[], int n, int i){0 ^1 J  H. v/ w
    if(i >= n){) j& k' t" B) J$ \7 |- C
        return;
9 r) ~. W: }8 L, z5 H& m; f    }
- {: I4 H4 o# k" ^7 E    int c1 = 2 * i + 1;//左子节点的下标
8 {: G) p' S  L( _2 @$ O# _8 b5 z5 b    int c2 = 2 * i + 2;//右子节点的下标) G# T3 B$ ^# A
    int max = i;//假设父节点是最大的
! c; b% s% c# a: U( c3 k0 F+ |    //找出最大值的下下标
& I% u. {" F6 Z1 R# c  P    if(c1 < n && tree[c1] > tree[max]){
7 G7 S+ g6 U8 r3 p5 o& u        max = c1;
9 E: w+ @" S4 C7 N1 i2 C    }
" e$ T: ?, X' P* L$ t! |. F    if(c2 < n && tree[c2] > tree[max]){1 P6 L3 ?1 K- f; x# p
        max = c2;
0 G* _' e6 K& l! h( L, e' {0 ^    }0 x& E! U( z2 a
    if(max != i){//如果最大值不是父节点,需要做换位置操作" w: f( z  k: d0 x5 T
        swap(tree, max, i);0 a4 |2 g, [8 t. s* Y
        //此时,i节点被换成最大值了,符合大顶堆的性质; S# \8 [8 I( v1 P" n
        //但是换到下面的节点不能保证比他的两个子节点都要大* `8 w5 b1 D% }- J
        //所以被换位置的节点继续调整
, o( y. w! w3 M  G! D5 C. G        heapIfy(tree, n, max);
) F" k0 U1 E5 ]% p6 X* s- N8 m    }
  l! {; H" b6 b5 y- L8 ]: ?}" z/ v5 m2 O6 s3 E# u
# \# H- N; T4 N6 s; P0 N
/**
) K# T* w8 q$ X' t * 完整构建大顶堆
% R! W& i4 d" u* _: { * @param arr 用于构建堆的数组" d9 B! r/ K7 Y3 e/ M" d# \6 [
* @param n 堆的最后一个节点的下标
  l% b5 }4 W+ G3 ` */1 E% A& l2 ^. Z% h2 U. @. C- n
private static void buildHeap(int arr[],int n){
$ j; b2 T. w9 s2 ]    int lastNode = n - 1;. P- j0 e! O2 U, S0 ]+ B
    int parent = (lastNode - 1) / 2;% _6 i) V9 I$ p2 R; p; r) o" J8 F. G
    for (int i = parent; i >= 0; i--){
) m8 Z1 [8 \( _  A/ @, M: V        heapIfy(arr, n, i);
9 L8 x- P: l8 x& w    }
' L7 W1 T- ?' a: S, R& h' }}. d1 `4 O) S6 E; O0 p( Y4 z3 f8 ]
1 j# p! D7 N% n: `, M; U/ i
/*** F0 k) k) J, K8 q
* 堆排序
& l& ~* j4 z- a9 _ * @param arr 待排数组. b% [; L. Z: e9 m+ c1 M: W
*/
+ q9 f, e0 h( ~4 G# B/ k6 r  [! jpublic static void sort(int arr[]){
2 K( N9 n) Y& l' h) M    buildHeap(arr, arr.length);//先构造大顶堆
8 e8 u0 n  ~2 P. Y! o    //每次构建堆后将根节点和最后一个节点进行交换
4 t8 S) q  T( F5 R/ X6 q9 d    //然后砍断最后一个节点
; ]% m  N6 v# x6 O* [- K    //所以从最后一个节点向前循环/ A, @% N2 M1 e. k+ n- Y( |! g
    for (int i = arr.length - 1; i > 0; i--){0 P/ ]* p' Q5 E# B% H
        swap(arr, 0, i);+ \$ e& i* A1 `- ^" n9 \5 S) ?
        heapIfy(arr, i, 0);
4 k2 V  r7 L* t- B: M    }
  p/ s( C- t8 _7 w: g6 B& ~8 U}  M8 n: U2 j/ u! z3 h
————————————————
9 Y) ~1 e+ z4 ?$ \+ R( c版权声明:本文为CSDN博主「qq_34912889」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。% V9 r7 v% T+ e! o2 j5 ^0 r7 }
原文链接:https://blog.csdn.net/qq_34912889/article/details/1056906448 E% s1 E( x& V# }' m
! _" Y7 w$ q; i- g& P6 a% M

# [! N0 _9 i" W- K$ y




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