数学建模社区-数学中国
标题:
十大经典排序算法之堆排序(Java语言)
[打印本页]
作者:
杨利霞
时间:
2020-4-23 15:00
标题:
十大经典排序算法之堆排序(Java语言)
. a+ X: K8 W% u; P! K) c1 H
十大经典排序算法之堆排序(Java语言)
( _! M4 ~0 X3 c2 q3 m4 [3 c
文章目录
) U1 F/ n7 f- a
4 U* C1 z4 ?+ A. y
什么是堆
! Z0 Q. C9 I4 ?9 Z
如何进行堆排序呢
8 y8 Y' B9 q3 y5 O3 v
用数组构建一个堆
5 |8 Q% o/ @) H2 R
上代码
! @2 U1 X% e0 ^) a
什么是堆
* D6 `8 x+ d; L5 t/ M2 }( b9 w
8 a6 H: C! D9 b' U4 l
在了解什么是堆之前一定要先了解什么是完全二叉树
% I+ o3 Q* M* c0 v3 s2 S, M4 v3 D
看一下百度百科的介绍
0 L6 e! ?; E2 k$ [
$ ?9 ^. ?9 q! w3 U6 ^% M$ _
若设二叉树的深度为h,除第 h 层外,其它各层 (1~h-1) 的结点数都达到最大个数,第 h 层所有的结点都连续集中在最左边,这就是完全二叉树。
4 o7 ~. C2 I( U
百度百科拗口版性质介绍,能看懂上面的就行,下面的大概看下
; S1 o" L3 Z' g0 L
+ o) {6 v5 u e! O- i
完全二叉树是由满二叉树而引出来的。对于深度为K的,有n个结点的二叉树,当且仅当其每一个结点都与深度为K的满二叉树中编号从1至n的结点一一对应时称之为完全二叉树。
, C& G& {5 M3 B' S
(1)所有的叶结点都出现在第k层或k-l层(层次最大的两层)
* Z( Z }* _9 S4 a% @5 y
(2)对任一结点,如果其右子树的最大层次为L,则其左子树的最大层次为L或L+l。
$ N4 G! m5 Q+ R4 x' K; s0 G2 }3 d
一棵二叉树至多只有最下面的两层上的结点的度数可以小于2,并且最下层上的结点都集中在该层最左边的若干位置上,则此二叉树成为完全二叉树,并且最下层上的结点都集中在该层最左边的若干位置上,而在最后一层上,右边的若干结点缺失的二叉树,则此二叉树成为完全二叉树。
! @7 k/ j- X: Q% O) W
那么在了解到什么是完全二叉树之后,我们再来看什么是堆
) v4 \8 n$ }$ I0 a( U F7 V
堆有以下两个性质
) \2 n, J& A: s' @! K, [) X
+ C* q9 V8 {2 M- J7 k* }
1 堆中某个节点的值总是不大于或不小于其父节点的值;
9 s8 k8 r5 I& c
2 堆总是一棵完全二叉树。
# |+ p2 w) ?* n: b G3 f7 r2 A: q& |
其中堆顶就对应二叉树的根
: [: S! z0 p! D' r- o& O6 j V; ?
. U, p+ u+ E( u& N
堆又分为大顶堆和小顶堆,根据堆的第一个性质来进行区分
$ {; l6 B0 M* I; a( V) M
. E9 z: J: h3 Z+ X" `3 C
当堆中某个节点的值总是大于它的子节点的时候这个堆为大顶堆,反之为小顶堆
' d8 v+ R* T* ?+ I
如何进行堆排序呢
+ F9 n. E" d8 `+ ~% K# F
9 T8 B7 }* [0 E+ U% w w0 m' Q
堆排序,其实就是每次构造出一个大顶堆或者小顶堆,然后取出堆顶的值,再将剩下的值重新构造成大顶堆或者小顶堆,最终到堆里的值全部取出来,取出来的数就是排好序的
9 I& r- m3 B+ b8 \$ a' F
8 x& q$ |7 W6 K5 N' M8 S, ^3 `
用数组构建一个堆
N' l4 h: G! \
3 j8 Z# N1 c) b
因为堆是一颗完全二叉树,所以我们可以用数组来对其进行存储
& C, V' k5 u# I* J' v; H
对于用数组存储的二叉树,我们可以用如下方法来定义:
9 z, ]) o( ?5 r4 i
假设当前节点的下标为 n
0 `4 x! q7 Q% o; f2 o
' _0 n3 U( h$ p, y
1、那么他的左子节点的下标 2*n + 1
, ]. `4 C1 g. ]! Y
2、那么他的右子节点的下标 2*n + 2
. t( @9 h0 H! B t8 [! ^4 ~
3、他左边的节点是 n-1,如果当前节点是第 h 层的最左节点,那么第h-1层的最右节点的下标就是 n-1
# `2 H+ }% h7 L# g- t
4、根据1、2可以推出来n节点的父节点是 (n-1)/2,不管当前节点是父节点的左子节点还是右子节点,都用 (n-1)/2就行了,因为整型数字相除小数点后面的会被截断
% S, E* a+ f5 F0 d% ?
那么有了上面四条性质,我们就可以开始动手了
) `+ b/ ~5 @. `# \- ]5 o1 e2 o! v
- o1 ^+ k1 n4 ?+ P
1、假设我们要构建的堆是大顶堆,那么根据大顶堆的性质,任意节点都比它的左右子节点要大,所以我们肯定有个heapify方法,该方法调整指定节点和其子节点的位置,并且继续调整被调整的子节点和孙子节点的关系,直到没有调整或者到数的最底层
* @( p3 e H2 _9 \
2、然后我们要有构造大顶堆的方法,构造大顶堆就是从最后一个节点的父节点开始调整,接设最后一个节点的父节点是n,那么我们就将n,n-1,n-2 ··· ··· 0,这些节点逐次,从大到小调用heapify方法,这些节点都调整完成后,大顶堆就构造完成了
/ {& X' D* G X4 \# i
3、接下来就开始将堆顶和堆尾互换,并砍断堆尾的操作了,由于互换之前,这是一个符合条件的大顶堆,但是换完只有只有一个堆顶这里不满足了,那么我们重新调整一下堆顶的三个元素就可以,还是调用heapify方法,这个方法会自上而下的重新调整堆,使其成为一个大顶堆
9 V& ?+ |4 m# C1 T. z1 J
6 ]$ u# l1 T8 a4 l4 y+ B# R$ B" Y
堆排序的性质
2 u, e/ C" ]9 j& a% l8 X0 [
2 [! p- H8 k! u9 H
中文名称 英文名称 平均时间复杂度 最坏时间复杂度 最好时间复杂度 空间复杂度 稳定性
% L2 ]. [) g" j
堆排序 Heap n*logn n*logn n*logn 1 不稳定
5 Y$ x8 E4 h; _2 S
上代码
0 i! Q$ P( }" c7 g6 R5 l
1 }4 b5 Z+ g' s
/**
% S& {) I. y+ [6 R l
* 交换第n和m个元素
3 r% e/ u% p! ~4 Z6 q1 Q
*/
) T3 s* P# W( R8 E8 A0 w1 g
private static void swap(int arr[], int n, int m){
) J2 a9 T$ k$ o! u% b: ~
int temp = arr[n];
5 X; Q9 F/ n/ \
arr[n] = arr[m];
8 {% x( C7 R7 G# n* E
arr[m] = temp;
. L) L3 Z" l/ Z
}
. e y! Z7 k" v
. D- N; U1 X4 W7 e6 i
/**
- n- G/ p# l- p. t. n" c+ K
* 调整指定节点和其子节点
: z3 z& a2 J+ d7 T7 e! T
*
@param
tree 整棵树
' S, I! V! u. y2 Z
* @param n 数组长度,树的元素个数
" C( D" ~, r/ r7 }: Q
* @param i 要调整的节点的下标
7 W; z5 O9 o1 q) A
*/
# ?& H; [5 U* T6 Q! [& B2 f
private static void heapIfy(int tree[], int n, int i){
! ]; n" C, ^- t+ s
if(i >= n){
( B. d, q9 C: s2 _0 |6 F- L' E
return;
. B8 g- o; k+ x5 { v4 c+ `7 a
}
( @! P1 p7 G3 o( I. c
int c1 = 2 * i + 1;//左子节点的下标
5 `9 @+ P2 l4 V
int c2 = 2 * i + 2;//右子节点的下标
( O+ H% J2 e( W9 {# ~% O4 k- q
int max = i;//假设父节点是最大的
9 F$ P3 b" k4 z) C3 l& l
//找出最大值的下下标
/ h# z l0 c, J: x8 E# `
if(c1 < n && tree[c1] > tree[max]){
1 `7 C9 w( a( P) f7 N) L
max = c1;
# G: X! V- U8 e0 ~- {
}
- p% p7 r4 ?/ `1 K( \8 Q
if(c2 < n && tree[c2] > tree[max]){
* W9 T7 K4 R6 h* `# n7 [
max = c2;
* I3 v- [5 q. A5 }1 i
}
7 I+ u: `3 w H& d1 y& K6 g
if(max != i){//如果最大值不是父节点,需要做换位置操作
9 C' E0 ~# ~+ P" I
swap(tree, max, i);
0 ^0 _8 i6 y) {4 V8 _! X
//此时,i节点被换成最大值了,符合大顶堆的性质
5 g" p; Q2 b* y y1 J/ @9 t
//但是换到下面的节点不能保证比他的两个子节点都要大
+ r. ]- i* G7 I+ T
//所以被换位置的节点继续调整
) `% y6 V& f& U8 A+ G `
heapIfy(tree, n, max);
4 d' R2 V& o7 ?9 z- f. e' D
}
1 d0 a# d8 [0 b2 ^$ J. V
}
( Z# `* W: |# R/ z
- z$ Z4 q6 y9 f- ^ j2 r
/**
" E; E* O5 F' E: j4 d9 Y/ E$ D
* 完整构建大顶堆
6 x( e' o0 t- z0 }) D w* A2 S
* @param arr 用于构建堆的数组
' j. }. {0 _2 Y) F3 M
* @param n 堆的最后一个节点的下标
4 G' c7 D; R9 ~& @
*/
! M1 U1 T ?8 v$ p7 ^# }4 C
private static void buildHeap(int arr[],int n){
* g" I( y7 C# S( }# Y- }" g) y
int lastNode = n - 1;
5 V9 `, ]7 c+ ?; E7 d
int parent = (lastNode - 1) / 2;
( g/ L+ d. I* G: D/ [
for (int i = parent; i >= 0; i--){
9 A" Q' k3 ~# B' i$ }
heapIfy(arr, n, i);
J& _+ A- u6 k
}
( Z, F4 ?/ J: U ^5 o
}
( @5 b- k2 X3 z( W- S% y
, _" {& `1 M# I' y9 `
/**
% \2 G, c+ |8 a( d2 s0 D2 x0 s
* 堆排序
; `: S3 n9 O7 D
* @param arr 待排数组
$ W9 w( X+ u# v; y" l
*/
+ w, C; ^* s( j6 h5 X4 a
public static void sort(int arr[]){
* @: \6 Z- q4 U% c: }
buildHeap(arr, arr.length);//先构造大顶堆
# O$ i( t: W% i) y2 O; V+ y
//每次构建堆后将根节点和最后一个节点进行交换
& q( R; |# D1 J# f! o: k! M1 D9 K
//然后砍断最后一个节点
0 @. W3 G% r: D& ?
//所以从最后一个节点向前循环
5 s) l& Z' R- q9 j& {& N
for (int i = arr.length - 1; i > 0; i--){
4 X$ e* t9 }! Y2 `: R( w8 T
swap(arr, 0, i);
: V8 w7 w2 \7 }2 }& t* r6 x3 _$ o
heapIfy(arr, i, 0);
! `$ o9 _. } g
}
8 H! z) ^% O" U. {4 [! _+ B0 D
}
: p5 v1 d; _' b, k% i4 B
————————————————
0 }8 Z4 J$ y `' F9 _2 q0 M
版权声明:本文为CSDN博主「qq_34912889」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
( t4 ~; \/ x7 k1 t0 Y B" L
原文链接:https://blog.csdn.net/qq_34912889/article/details/105690644
, p$ N, b% g E) b2 ]: u: K' L
; c5 G* S& {6 [( K- u6 i+ a
, l$ R/ z* U& d7 H% l
欢迎光临 数学建模社区-数学中国 (http://www.madio.net/)
Powered by Discuz! X2.5