数学建模社区-数学中国
标题:
十大经典排序算法之堆排序(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* C
1 堆中某个节点的值总是不大于或不小于其父节点的值;
) 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) j
1、那么他的左子节点的下标 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/ }( R
4、根据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( w
private 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 i
4 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