数学建模社区-数学中国

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

作者: 杨利霞    时间: 2020-4-23 15:00
标题: 十大经典排序算法之堆排序(Java语言)
- r) P9 E* N  m& d- i6 d* P
十大经典排序算法之堆排序(Java语言)- D. P& J5 h  ]5 L7 I9 P
文章目录* ~# g  X: a' N0 _1 k

1 E- B: z  ^, F$ t( I" u什么是堆
  @) A4 `' E* p6 v如何进行堆排序呢4 Q' T! W7 W: Q# H0 D$ n: @8 |
用数组构建一个堆
% }# _! Y- ?7 S0 a9 Z# a  N上代码
9 W+ h3 l& S0 D. ~9 w# M* M' K什么是堆/ x: O$ N& D9 h! n( i" J9 R0 l
$ b: q7 l% o* S$ K# ?$ r5 c8 j% S0 a
在了解什么是堆之前一定要先了解什么是完全二叉树
4 m# M  q6 j& u# s看一下百度百科的介绍1 T$ u- ^  z& b  R  ?; t4 w

4 Y/ P/ e1 T4 w) Q8 Z+ h. r若设二叉树的深度为h,除第 h 层外,其它各层 (1~h-1) 的结点数都达到最大个数,第 h 层所有的结点都连续集中在最左边,这就是完全二叉树。
6 q9 E3 Z% l- r. H! i8 G/ ~百度百科拗口版性质介绍,能看懂上面的就行,下面的大概看下
. A. T6 Z" `4 f& }" p) `
6 k. `6 j( {9 m) q2 Z完全二叉树是由满二叉树而引出来的。对于深度为K的,有n个结点的二叉树,当且仅当其每一个结点都与深度为K的满二叉树中编号从1至n的结点一一对应时称之为完全二叉树。$ R6 p$ o1 s+ d  ]
(1)所有的叶结点都出现在第k层或k-l层(层次最大的两层)( x3 z/ d* K* Q) f( a, p9 {- N
(2)对任一结点,如果其右子树的最大层次为L,则其左子树的最大层次为L或L+l。
9 Y% @9 T# r0 |一棵二叉树至多只有最下面的两层上的结点的度数可以小于2,并且最下层上的结点都集中在该层最左边的若干位置上,则此二叉树成为完全二叉树,并且最下层上的结点都集中在该层最左边的若干位置上,而在最后一层上,右边的若干结点缺失的二叉树,则此二叉树成为完全二叉树。
" }- _% g) c/ v/ j$ c# c那么在了解到什么是完全二叉树之后,我们再来看什么是堆
- }8 c4 ]2 s; Z堆有以下两个性质
7 l7 g2 `1 Z( D0 p* d/ Q
, E. [! {2 A' ]2 B0 F+ \+ l1 堆中某个节点的值总是不大于或不小于其父节点的值;+ E, @- y& P* U  u2 R/ A8 ]
2 堆总是一棵完全二叉树。
. e! [, [6 G6 K; P1 }其中堆顶就对应二叉树的根
& p( n  k$ A0 i) J) [
) M) l, e, X% V1 d1 g3 M! J堆又分为大顶堆和小顶堆,根据堆的第一个性质来进行区分3 F0 ?5 M* _* i9 \! V- B0 @
' c1 M" I0 U: X3 n  T+ H
当堆中某个节点的值总是大于它的子节点的时候这个堆为大顶堆,反之为小顶堆4 s. {( n& z: a1 r% y
如何进行堆排序呢; m0 q+ h" ]5 R1 M

7 J+ {6 _) q5 v6 l堆排序,其实就是每次构造出一个大顶堆或者小顶堆,然后取出堆顶的值,再将剩下的值重新构造成大顶堆或者小顶堆,最终到堆里的值全部取出来,取出来的数就是排好序的  r3 ?5 v: @, V, I0 I6 u; u

# U+ g. j( U& k* u用数组构建一个堆
6 m4 A& q% Q) D5 ?, Y% Q. P3 v$ V8 ?' w6 g7 G$ w
因为堆是一颗完全二叉树,所以我们可以用数组来对其进行存储
9 e0 X8 `% S6 l对于用数组存储的二叉树,我们可以用如下方法来定义:  ^& C0 c0 \* E5 d( j* P
假设当前节点的下标为 n& f) N9 o! r& H, `; x

0 \$ S( e" t  T, p+ T8 t! _! i* W1、那么他的左子节点的下标 2*n + 1
7 d! ~" k$ B. t: N4 M( e" Y; {2、那么他的右子节点的下标 2*n + 2
* ^4 a3 |! o% g8 o. V3、他左边的节点是 n-1,如果当前节点是第 h 层的最左节点,那么第h-1层的最右节点的下标就是 n-1
# t" W0 n% r4 d, V/ O* o8 H4、根据1、2可以推出来n节点的父节点是 (n-1)/2,不管当前节点是父节点的左子节点还是右子节点,都用 (n-1)/2就行了,因为整型数字相除小数点后面的会被截断$ R) `/ {. C: N. p" O! a
那么有了上面四条性质,我们就可以开始动手了8 ^( e0 \( ?' U: t! w7 o0 D2 o1 Y. g
6 h4 Y2 u8 Z& ]
1、假设我们要构建的堆是大顶堆,那么根据大顶堆的性质,任意节点都比它的左右子节点要大,所以我们肯定有个heapify方法,该方法调整指定节点和其子节点的位置,并且继续调整被调整的子节点和孙子节点的关系,直到没有调整或者到数的最底层
; e. [( {" j: d# `& ?8 t, u5 k6 y& h2、然后我们要有构造大顶堆的方法,构造大顶堆就是从最后一个节点的父节点开始调整,接设最后一个节点的父节点是n,那么我们就将n,n-1,n-2 ··· ··· 0,这些节点逐次,从大到小调用heapify方法,这些节点都调整完成后,大顶堆就构造完成了; i) K/ P) E2 ]% `7 i
3、接下来就开始将堆顶和堆尾互换,并砍断堆尾的操作了,由于互换之前,这是一个符合条件的大顶堆,但是换完只有只有一个堆顶这里不满足了,那么我们重新调整一下堆顶的三个元素就可以,还是调用heapify方法,这个方法会自上而下的重新调整堆,使其成为一个大顶堆: E; a$ l6 f' i2 I' y+ x/ O0 B

2 A' l" v, Q$ o8 L$ S堆排序的性质( z# B0 @- c6 Q9 g! B* Z% \

; h1 W) i; r  k$ m中文名称        英文名称        平均时间复杂度        最坏时间复杂度        最好时间复杂度        空间复杂度        稳定性+ I$ k; a% v' Q
堆排序        Heap        n*logn        n*logn        n*logn        1        不稳定+ W9 Z- c9 F6 W: k  q
上代码0 I9 U4 o0 i# |- I6 Y0 Y/ a

! _3 ]. O0 s% |/**
( t# Q* x8 @/ s9 C7 B. F * 交换第n和m个元素
, |% P$ Z5 _  p6 O8 {# M */
6 |+ E# c6 M: f0 pprivate static void swap(int arr[], int n, int m){0 X, {$ w& X0 @, b% e9 X
    int temp = arr[n];8 y/ p# N# v3 a9 o
    arr[n] = arr[m];4 w+ e0 f, C% i$ i% f) V) F+ r
    arr[m] = temp;) T- L" Y% D, \8 k: o3 _/ D, V
}$ z3 o  W5 q) N

& t3 R# |5 c: x: O2 G/**
: w1 k/ c8 Z# l$ x * 调整指定节点和其子节点
! `* C6 F) v* L3 r. x* f" p% e * @param tree 整棵树
. c4 j- p7 F/ a, b& K * @param n 数组长度,树的元素个数+ {/ a7 W. C; C- e; n0 u$ m
* @param i 要调整的节点的下标
* Y0 O* G; s1 Z5 |: f& _ */
2 V3 Q2 {5 p" W1 Mprivate static void heapIfy(int tree[], int n, int i){
9 z0 \; }, m6 x5 U    if(i >= n){
+ v: R- h/ T8 f# G, U; r        return;
. i, Q6 }1 a) _3 B; p7 J    }! ~: c4 k/ |+ d, o
    int c1 = 2 * i + 1;//左子节点的下标
$ E. t2 z' {9 P4 O, y9 j    int c2 = 2 * i + 2;//右子节点的下标' _. h; `- r9 h; m: p7 K6 `
    int max = i;//假设父节点是最大的" z9 L7 y0 x( Q: E; ^
    //找出最大值的下下标
* ^- t5 e) k4 V& Q: s    if(c1 < n && tree[c1] > tree[max]){0 i2 z5 I2 ?4 {/ r$ a" i4 [
        max = c1;" @$ J- j! D) V; U
    }
1 K( m0 w# W8 a2 n    if(c2 < n && tree[c2] > tree[max]){" _. v$ R; d  g- J
        max = c2;) L; l" q! i. w$ u2 y+ m
    }
2 S( ^6 s( D, G" e, y8 A- S6 b    if(max != i){//如果最大值不是父节点,需要做换位置操作- l7 U$ ?% f( ]
        swap(tree, max, i);
& |) [% \$ I, R3 `- f        //此时,i节点被换成最大值了,符合大顶堆的性质
8 w5 w; I: J/ D6 V/ L        //但是换到下面的节点不能保证比他的两个子节点都要大+ e8 y1 _, J5 p
        //所以被换位置的节点继续调整6 X$ @) {# O$ o* N5 O" k
        heapIfy(tree, n, max);7 P8 O5 ~* |  V/ q2 w/ _* X
    }* }; M6 m$ D" y* C7 j5 \6 o7 |  {- b
}7 {1 b. e" m1 q- }

# \6 f# ?. r  e& G/**' U  }) n4 a7 [- C& G$ t
* 完整构建大顶堆
& {2 o* N% z1 L; ?* k * @param arr 用于构建堆的数组, I' Z- K( X2 k% f
* @param n 堆的最后一个节点的下标
5 o+ B6 {9 y4 I2 G */& N8 ?* i. `( s  @; e, V$ H
private static void buildHeap(int arr[],int n){
% J7 d/ ?8 D0 W9 f6 Z$ z2 p8 K    int lastNode = n - 1;
/ z" q9 Y2 a7 d5 m( Y5 V& ?    int parent = (lastNode - 1) / 2;# x" i0 c3 X: N9 a- J) _  S
    for (int i = parent; i >= 0; i--){
5 C4 ?6 l8 F/ R: @1 c        heapIfy(arr, n, i);5 t3 G* ?8 G) u' g5 G1 {
    }: U0 B5 z. n. J/ D
}2 l" y. ~! Q1 Z
3 F/ T% e" r; o5 u! Z/ O
/**- l1 }8 ?8 n# S1 B( s4 S+ A
* 堆排序
* P! F& H" v8 Z * @param arr 待排数组
+ C1 n; H' [# ~$ b7 m9 F *// T( P+ }- {0 |) _
public static void sort(int arr[]){
' w5 }- S2 d0 r5 O: M    buildHeap(arr, arr.length);//先构造大顶堆
' V( L+ ?4 _9 \    //每次构建堆后将根节点和最后一个节点进行交换
* ?1 S2 _- D% O( Y    //然后砍断最后一个节点
/ K/ H9 {1 c9 e1 y* w* K( U    //所以从最后一个节点向前循环: a+ K2 @/ B1 e' z6 e
    for (int i = arr.length - 1; i > 0; i--){
5 Z5 c! ]& K. U$ r% l4 k/ s        swap(arr, 0, i);5 k. E2 M- t6 _7 ^$ s" L
        heapIfy(arr, i, 0);
: K- M2 x1 e6 Y- G+ q    }
, _& F& v1 Z3 h1 U& {2 f}
$ |. \) j4 k) }5 S————————————————
  L8 I+ a* `( s  e) A版权声明:本文为CSDN博主「qq_34912889」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。. _4 \# A, K; s0 E+ z
原文链接:https://blog.csdn.net/qq_34912889/article/details/105690644
5 R- W5 x2 B+ O0 f1 u+ _
+ p! a6 h- |, N6 `$ T
& z: P9 J7 U. E7 k) G: T




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