在线时间 1630 小时 最后登录 2024-1-29 注册时间 2017-5-16 听众数 82 收听数 1 能力 120 分 体力 566869 点 威望 12 点 阅读权限 255 积分 175284 相册 1 日志 0 记录 0 帖子 5313 主题 5273 精华 3 分享 0 好友 163
TA的每日心情 开心 2021-8-11 17:59
签到天数: 17 天
[LV.4]偶尔看看III
网络挑战赛参赛者
网络挑战赛参赛者
自我介绍 本人女,毕业于内蒙古科技大学,担任文职专业,毕业专业英语。
群组 : 2018美赛大象算法课程
群组 : 2018美赛护航培训课程
群组 : 2019年 数学中国站长建
群组 : 2019年数据分析师课程
群组 : 2018年大象老师国赛优
9 _+ H- B3 |' ~ 十大经典排序算法之堆排序(Java语言)
+ m1 S Q" \* b 文章目录
% l! Y( f: j/ m8 h! c: L
: P2 A6 X) n: i4 G# x/ R: V! d 什么是堆
2 ~" ~" v/ P ]. h. }( w- ] 如何进行堆排序呢
( u" w+ t6 `- z, y4 ~2 c/ ~: R 用数组构建一个堆$ k* G1 m1 R2 R# R6 h) Q' a9 N
上代码
: c3 C, k( ~ a; V" l 什么是堆+ I$ S8 x% ~' ?. K& e% v6 ~
) a; ]+ B) D: a% \1 l: z G% R2 n- j
在了解什么是堆之前一定要先了解什么是完全二叉树
. d" D. U) w3 ~, y4 i% e 看一下百度百科的介绍* ]4 ?% i8 I# ~( S
1 W6 F0 K' r! n' P$ y) ` 若设二叉树的深度为h,除第 h 层外,其它各层 (1~h-1) 的结点数都达到最大个数,第 h 层所有的结点都连续集中在最左边,这就是完全二叉树。3 o$ W' R2 c/ |/ y. Z
百度百科拗口版性质介绍,能看懂上面的就行,下面的大概看下 o/ p$ R( X1 k
4 V* o# T0 p: b Z; q9 h! |3 g+ o 完全二叉树是由满二叉树而引出来的。对于深度为K的,有n个结点的二叉树,当且仅当其每一个结点都与深度为K的满二叉树中编号从1至n的结点一一对应时称之为完全二叉树。- e6 z6 A# b" k N2 |
(1)所有的叶结点都出现在第k层或k-l层(层次最大的两层)
: D9 c7 L& M, N (2)对任一结点,如果其右子树的最大层次为L,则其左子树的最大层次为L或L+l。
1 `3 Z3 x* B8 }% ]/ d9 h 一棵二叉树至多只有最下面的两层上的结点的度数可以小于2,并且最下层上的结点都集中在该层最左边的若干位置上,则此二叉树成为完全二叉树,并且最下层上的结点都集中在该层最左边的若干位置上,而在最后一层上,右边的若干结点缺失的二叉树,则此二叉树成为完全二叉树。1 j; M. K3 w4 K S- ]
那么在了解到什么是完全二叉树之后,我们再来看什么是堆
# x8 R" R2 N: I- D! Q 堆有以下两个性质1 N% B, `( F! u/ P6 g: Z8 a6 g
: E+ N, o0 s: U: Q 1 堆中某个节点的值总是不大于或不小于其父节点的值;+ i5 U8 V' ?0 [5 W$ q+ x8 g/ g
2 堆总是一棵完全二叉树。% Y2 i: m% r( {6 k2 S
其中堆顶就对应二叉树的根
2 k0 [' |/ d1 J1 D$ u4 w ! V1 q2 n8 O3 w! I6 }4 a
堆又分为大顶堆和小顶堆,根据堆的第一个性质来进行区分
' C4 W- b/ J: F2 e4 V: \5 v & ^9 Y9 K3 v1 M4 \! r
当堆中某个节点的值总是大于它的子节点的时候这个堆为大顶堆,反之为小顶堆0 \8 k. x; f* u C
如何进行堆排序呢
" W* F# p+ ^6 _, r: `- { : T8 ]' s9 a8 l. p c3 A. T
堆排序,其实就是每次构造出一个大顶堆或者小顶堆,然后取出堆顶的值,再将剩下的值重新构造成大顶堆或者小顶堆,最终到堆里的值全部取出来,取出来的数就是排好序的7 m0 d: r6 h9 O4 V" T2 _: G
* `* f! t' _0 T* G4 `$ |' G5 [
用数组构建一个堆
) d* K, O2 m1 k4 T8 Y- ^9 @/ L0 I* ? ' n+ C1 a! u7 o% q z$ X
因为堆是一颗完全二叉树,所以我们可以用数组来对其进行存储
( v; n3 c5 F; t 对于用数组存储的二叉树,我们可以用如下方法来定义:0 ], h0 \: i) R' C: O
假设当前节点的下标为 n2 V, n8 p0 B% u# q' T& ^3 H
4 z5 u2 R) _! }+ [( C. K: T! ~
1、那么他的左子节点的下标 2*n + 11 E' D! y; I1 U7 r
2、那么他的右子节点的下标 2*n + 2& e) j' M" p* C! s8 s
3、他左边的节点是 n-1,如果当前节点是第 h 层的最左节点,那么第h-1层的最右节点的下标就是 n-19 S5 U$ Z/ H5 j7 f5 o
4、根据1、2可以推出来n节点的父节点是 (n-1)/2,不管当前节点是父节点的左子节点还是右子节点,都用 (n-1)/2就行了,因为整型数字相除小数点后面的会被截断
+ t. {5 a. d7 c9 s5 w# q( _8 }9 ^- k 那么有了上面四条性质,我们就可以开始动手了
' H" F- n( N' { Y0 F
$ C; f% t, q0 r8 C* T5 [9 p+ P' U 1、假设我们要构建的堆是大顶堆,那么根据大顶堆的性质,任意节点都比它的左右子节点要大,所以我们肯定有个heapify方法,该方法调整指定节点和其子节点的位置,并且继续调整被调整的子节点和孙子节点的关系,直到没有调整或者到数的最底层8 p9 X% a+ f: y& E
2、然后我们要有构造大顶堆的方法,构造大顶堆就是从最后一个节点的父节点开始调整,接设最后一个节点的父节点是n,那么我们就将n,n-1,n-2 ··· ··· 0,这些节点逐次,从大到小调用heapify方法,这些节点都调整完成后,大顶堆就构造完成了0 _9 P) s" P) ~/ z+ u! j
3、接下来就开始将堆顶和堆尾互换,并砍断堆尾的操作了,由于互换之前,这是一个符合条件的大顶堆,但是换完只有只有一个堆顶这里不满足了,那么我们重新调整一下堆顶的三个元素就可以,还是调用heapify方法,这个方法会自上而下的重新调整堆,使其成为一个大顶堆9 l7 x+ I; H2 Z/ o% x. b
- e( i1 {1 i2 m" t& S, d 堆排序的性质
) l! _1 X! L2 _+ b
`* X# e* t1 N* l3 {. ? 中文名称 英文名称 平均时间复杂度 最坏时间复杂度 最好时间复杂度 空间复杂度 稳定性
9 ~& p: {% J, R* m# L0 A 堆排序 Heap n*logn n*logn n*logn 1 不稳定
; H$ i' s7 F% v: d 上代码
9 w- U6 O+ P" [7 l+ }
6 p+ I8 ?$ c- n9 j( B4 C8 L /**
- H4 d7 P0 P" H+ {- [) r * 交换第n和m个元素
, M& W8 ]4 T) p& R+ p */
4 _0 d+ J+ i8 ^0 W1 n) \1 B private static void swap(int arr[], int n, int m){
! r9 ?7 U. X% m9 u# B; @ int temp = arr[n];
4 E! ?+ e1 {2 `/ E7 d/ q4 R arr[n] = arr[m];
( b" b" I6 h8 C N# z$ V: y arr[m] = temp;
% x- `, `+ ~, P8 | }0 u0 v ?4 t4 f
) c8 N) k- X% [7 U+ b" C4 A
/**. N- o8 s. V" E i2 Y
* 调整指定节点和其子节点
$ Q4 }, }3 p& D' c( h7 p& h * @param tree 整棵树
6 @% Y# ~ p, [, ?6 F * @param n 数组长度,树的元素个数/ b3 s q& z3 m' a
* @param i 要调整的节点的下标
9 S% Z {7 s6 ]- a+ g/ c9 J6 v */5 c. _: [1 X+ j0 A; U2 Y+ b
private static void heapIfy(int tree[], int n, int i){
+ F) e8 }) p* I: _ if(i >= n){
; n: B% d2 y3 U return;
; I) w& X; L0 `5 ^ }$ J7 [: @2 }1 {5 J4 B& ]* r
int c1 = 2 * i + 1;//左子节点的下标
( R4 U- r G6 ]9 o int c2 = 2 * i + 2;//右子节点的下标- c- h! {! a, x: r
int max = i;//假设父节点是最大的
: Z/ U2 z. w+ ^- L9 Y+ ?* _ //找出最大值的下下标
x6 `8 X9 I6 e9 }% A. F' a6 D if(c1 < n && tree[c1] > tree[max]){
9 }1 i* ^3 r+ H7 C) p! _8 ^ max = c1;
* W0 d* _" b; E+ ~6 o. l }
& K4 A5 }! u3 Q% {( ~7 L/ m if(c2 < n && tree[c2] > tree[max]){
( O# W2 s; Y5 V+ ] max = c2;% O; J) u- A: l6 Q }- M* r l
}9 D) i9 O) ?$ y, Y7 `9 H
if(max != i){//如果最大值不是父节点,需要做换位置操作8 ~" m' g9 }" U- ^8 D! f% X
swap(tree, max, i);
& O7 W) I, Q5 v2 s& S/ B# {! N //此时,i节点被换成最大值了,符合大顶堆的性质8 P# T# @6 t N K9 `5 V, o
//但是换到下面的节点不能保证比他的两个子节点都要大
7 {* W( ~2 l. v4 g) J //所以被换位置的节点继续调整, V0 G. Z/ N' j6 y, P1 C
heapIfy(tree, n, max);
2 h! ^2 ^* A1 a9 H$ z }5 C& A$ O- n* t* ?& O% b- J3 ?
}
1 L0 X# d- f, ^ N
0 m7 G0 Z0 y* h1 W! H0 q; g/ d /**
- ~, w/ K2 D/ t2 C$ P * 完整构建大顶堆8 |/ k) ~3 C; A, b( U
* @param arr 用于构建堆的数组
# X0 a" l% F: b- |, q" } * @param n 堆的最后一个节点的下标+ F" s( u4 i+ F. G' A8 `5 o# {
*/
1 e5 v# i7 y/ q4 g, o8 H2 F z* r6 y2 y private static void buildHeap(int arr[],int n){, v; U+ e- V, g4 g, d) D2 P
int lastNode = n - 1;
% S& j+ Q# [# }% j int parent = (lastNode - 1) / 2;
1 y; I8 Q& B* q1 O for (int i = parent; i >= 0; i--){
- D" ~9 A% ]1 Z9 H) |$ a0 _ heapIfy(arr, n, i);
/ m0 \" E" W, K$ O) l0 f }2 q, G( D8 {! }! ^
}& L9 Q0 [" P3 w$ q- j
; H9 K" w7 @8 P, M
/**
' t9 m {" X( p& m * 堆排序( f. x7 b# r: H7 E. d0 N+ k! [
* @param arr 待排数组0 F: J& } X0 o) @- e! F5 {
*/
, n, f; t- x4 k8 d8 V: ^ public static void sort(int arr[]){( f) f; c/ o# U8 e8 }; L1 \
buildHeap(arr, arr.length);//先构造大顶堆; H3 L; W" V2 H* q
//每次构建堆后将根节点和最后一个节点进行交换
4 s3 [# W) _5 h9 p( b; V //然后砍断最后一个节点6 ^) F7 `/ q; n0 i/ s( L# ]
//所以从最后一个节点向前循环* ]4 x9 Q% q2 ~: `3 T5 M/ L( M
for (int i = arr.length - 1; i > 0; i--){
0 y9 Q/ b+ c, q* I2 d! ?% t9 f swap(arr, 0, i);
9 q6 j& ]$ B. ^) ]2 |( O" r% k& p0 \- e heapIfy(arr, i, 0);! Q8 E/ A/ r# H
}! @2 B- N- H3 O& f* @6 v
}! Z0 T+ A' y$ }* V: H! D1 z8 v) D
————————————————& A6 _2 w* x* f d5 f
版权声明:本文为CSDN博主「qq_34912889」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
! g' n% P$ X& G5 R) o. H8 ] 原文链接:https://blog.csdn.net/qq_34912889/article/details/105690644
" E5 W- U) f: e2 L2 w
D' t2 u+ G8 c8 h7 A8 _ 9 t8 f- u) F! e, z$ J
zan