- 在线时间
- 1630 小时
- 最后登录
- 2024-1-29
- 注册时间
- 2017-5-16
- 听众数
- 82
- 收听数
- 1
- 能力
- 120 分
- 体力
- 566871 点
- 威望
- 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年大象老师国赛优 |
- z0 v0 o7 i. k2 O- s0 o十大经典排序算法之堆排序(Java语言)
( p7 A) o! p* @' Y文章目录
* t) V: j9 f. R/ |9 L, R, ?# R$ I- E4 y
什么是堆
6 v1 T7 Q1 [. O" J5 l, q* ?5 F' n如何进行堆排序呢
9 C4 ?- G7 ]; R1 x. g' n用数组构建一个堆
8 Z3 h, g& r- C: L( P' r% T. [. u上代码- ?/ k$ D8 ~# w; m; F5 v
什么是堆
( U, g9 X6 i7 h) z1 f
' W: b4 b1 F. X" `3 e在了解什么是堆之前一定要先了解什么是完全二叉树! H' h4 y7 g7 @& C" Z. q X
看一下百度百科的介绍3 ]2 i" h& W' u. n. j
5 n; a) Q8 q7 A. G
若设二叉树的深度为h,除第 h 层外,其它各层 (1~h-1) 的结点数都达到最大个数,第 h 层所有的结点都连续集中在最左边,这就是完全二叉树。
& v1 [, ?6 p, F百度百科拗口版性质介绍,能看懂上面的就行,下面的大概看下
& S3 I b- w8 W" t) f$ Y
3 X) S5 _( d E" H+ Q# Z* s p完全二叉树是由满二叉树而引出来的。对于深度为K的,有n个结点的二叉树,当且仅当其每一个结点都与深度为K的满二叉树中编号从1至n的结点一一对应时称之为完全二叉树。
5 n! m. {& k4 x/ W5 p2 A' ~3 ](1)所有的叶结点都出现在第k层或k-l层(层次最大的两层)- o3 R8 m2 Z9 d* ~
(2)对任一结点,如果其右子树的最大层次为L,则其左子树的最大层次为L或L+l。
% e1 N% O. r# S4 d# D一棵二叉树至多只有最下面的两层上的结点的度数可以小于2,并且最下层上的结点都集中在该层最左边的若干位置上,则此二叉树成为完全二叉树,并且最下层上的结点都集中在该层最左边的若干位置上,而在最后一层上,右边的若干结点缺失的二叉树,则此二叉树成为完全二叉树。
: c0 A3 [ V4 W6 Z7 W0 D那么在了解到什么是完全二叉树之后,我们再来看什么是堆
6 x' A' D( Q0 I: x4 P堆有以下两个性质
% G: h* n+ j* e( T% f, P# t
- v7 b* Y4 c; y V1 堆中某个节点的值总是不大于或不小于其父节点的值;
) W. [! \: L- o! R2 E/ l2 堆总是一棵完全二叉树。
6 h: k. K3 e. |其中堆顶就对应二叉树的根
7 y3 e/ e: m7 b) ?- Y3 Y% m1 J7 O3 O, s/ M1 p2 v3 { f
堆又分为大顶堆和小顶堆,根据堆的第一个性质来进行区分7 r# A. y/ f9 c; l0 B
h2 h; z4 _& n. a当堆中某个节点的值总是大于它的子节点的时候这个堆为大顶堆,反之为小顶堆
5 e) p6 j, h( L4 A! k; `' M/ U如何进行堆排序呢
% e, w* W( \- @% m- _% L' t
) K: I* A4 z7 i9 F' d堆排序,其实就是每次构造出一个大顶堆或者小顶堆,然后取出堆顶的值,再将剩下的值重新构造成大顶堆或者小顶堆,最终到堆里的值全部取出来,取出来的数就是排好序的
; O4 b/ p" D4 a- |
4 f+ f; n- o0 F4 I2 N7 h, k% l用数组构建一个堆
4 E c3 e+ O* \) _+ l; H9 S/ R( n5 ^' P5 O7 L1 a- D- }# B) U
因为堆是一颗完全二叉树,所以我们可以用数组来对其进行存储4 l$ r' q, I8 J6 G7 [) h
对于用数组存储的二叉树,我们可以用如下方法来定义:+ ^& ]# P7 M+ ]. ]5 `5 s
假设当前节点的下标为 n
& w1 L, Y# c, a6 h5 v- m+ W3 {5 E; C1 i" N6 v. k# f& d
1、那么他的左子节点的下标 2*n + 15 H/ C4 Z3 B- T( W M1 k$ h/ F' A
2、那么他的右子节点的下标 2*n + 2
f, | F4 h! g3、他左边的节点是 n-1,如果当前节点是第 h 层的最左节点,那么第h-1层的最右节点的下标就是 n-12 ^+ Y9 K* Y( S4 s( n) T$ I
4、根据1、2可以推出来n节点的父节点是 (n-1)/2,不管当前节点是父节点的左子节点还是右子节点,都用 (n-1)/2就行了,因为整型数字相除小数点后面的会被截断
1 E: M* b2 b1 |3 z- t; L' ]那么有了上面四条性质,我们就可以开始动手了- x! X/ H" i3 u% W; P0 {
# H3 ], F3 Y+ f8 i
1、假设我们要构建的堆是大顶堆,那么根据大顶堆的性质,任意节点都比它的左右子节点要大,所以我们肯定有个heapify方法,该方法调整指定节点和其子节点的位置,并且继续调整被调整的子节点和孙子节点的关系,直到没有调整或者到数的最底层
1 i1 m$ [# w! A& E0 [% w2、然后我们要有构造大顶堆的方法,构造大顶堆就是从最后一个节点的父节点开始调整,接设最后一个节点的父节点是n,那么我们就将n,n-1,n-2 ··· ··· 0,这些节点逐次,从大到小调用heapify方法,这些节点都调整完成后,大顶堆就构造完成了6 {9 o9 _( F, q
3、接下来就开始将堆顶和堆尾互换,并砍断堆尾的操作了,由于互换之前,这是一个符合条件的大顶堆,但是换完只有只有一个堆顶这里不满足了,那么我们重新调整一下堆顶的三个元素就可以,还是调用heapify方法,这个方法会自上而下的重新调整堆,使其成为一个大顶堆" _# Q) W! P/ r, i& X+ o: @' @
- N8 E3 J t+ {, _9 U
堆排序的性质 L) s! C1 x5 r3 c4 O
1 }, G% p; H7 p3 ]2 I. k中文名称 英文名称 平均时间复杂度 最坏时间复杂度 最好时间复杂度 空间复杂度 稳定性' W5 k& W3 Z6 w* i
堆排序 Heap n*logn n*logn n*logn 1 不稳定
7 j( [8 i+ v9 n- ]# i. X' G7 V+ Y上代码
$ Z* I7 \! T$ g2 Z+ x. t
1 y t, z) X* ~" _% L0 {/**
- v7 T' H9 v( A5 Q' t: F * 交换第n和m个元素( ^0 `( l1 F) M2 O
*/" \5 P+ P1 B6 d1 }0 w: E
private static void swap(int arr[], int n, int m){3 H% v( R8 h# N
int temp = arr[n];$ j0 Y7 _ n: I0 O
arr[n] = arr[m];# e2 Y: M ]5 I( i
arr[m] = temp;
, U! ?; ~2 k0 ?- ?}5 F1 P4 P7 p$ U" X- s4 G& Y
9 E! z x/ e0 R; r( E! d8 r; A: J
/**
3 ?5 u% ~" t9 j6 F: n * 调整指定节点和其子节点
2 N- F8 p. G5 M U * @param tree 整棵树
* N- y5 U ?3 \6 G9 f$ y * @param n 数组长度,树的元素个数( i0 D! E- Z: r% ^: v/ B
* @param i 要调整的节点的下标# K- Q6 c8 [$ _% I: s+ z
*/
& I; D, h* p9 P0 E7 A& Yprivate static void heapIfy(int tree[], int n, int i){
_2 i0 E( l X3 j9 i, F if(i >= n){
2 d: X0 E. I9 d return;/ G) e- B$ v0 R& E0 T
}
/ K( o& Q) p: b: @5 x5 F6 ]# C; h int c1 = 2 * i + 1;//左子节点的下标
* b5 d; c# l1 n7 ?/ w# f6 ` \ int c2 = 2 * i + 2;//右子节点的下标! R3 `) F2 T0 U5 [- c$ e
int max = i;//假设父节点是最大的! D2 D8 X9 ]) u, a" n
//找出最大值的下下标; z* s p# E- _1 [6 r- O {6 m* d
if(c1 < n && tree[c1] > tree[max]){' y* W* A4 r: D7 y. `2 \
max = c1;
4 i# g7 {; V( x }! F, ?, s: ^4 B3 D* a m
if(c2 < n && tree[c2] > tree[max]){' \5 |3 X& y( ^8 ^9 W7 ?
max = c2;/ N, m5 d2 Z% K% W* ^
}
, \, Z" d9 W; d" A- c; E; E if(max != i){//如果最大值不是父节点,需要做换位置操作
/ n# K w, ^+ }2 r swap(tree, max, i);
( }3 w, E3 M4 s# w, |; p( o5 } //此时,i节点被换成最大值了,符合大顶堆的性质
3 s' v3 d. L) Y //但是换到下面的节点不能保证比他的两个子节点都要大
+ J! O# X; i/ T //所以被换位置的节点继续调整
5 u E8 v) {& ~ heapIfy(tree, n, max);
- X+ H S/ s5 ]0 |, X }
7 p+ y0 |( e$ ~8 H1 X/ U: t}: {2 E$ T' \7 S. X, j; s U7 ?
( d ^! \8 F5 B1 X$ F& Q, l/ x
/**
0 K3 \7 E4 W" e * 完整构建大顶堆% v5 {$ o- p6 f! P, D
* @param arr 用于构建堆的数组
4 [) E7 o5 L( {7 k * @param n 堆的最后一个节点的下标
, _ y0 Q4 N0 j$ E7 {1 q& t9 ] */$ _7 V0 a# W# y Q9 z; ^
private static void buildHeap(int arr[],int n){. o* J3 j$ F& j% E
int lastNode = n - 1;
1 h. U) F# p" e! C0 P8 I4 Q int parent = (lastNode - 1) / 2;
3 k# j5 J' u0 u( v: ] for (int i = parent; i >= 0; i--){
0 m( d2 E* f8 I9 A heapIfy(arr, n, i);. G4 Z- h, n8 g; Y! n8 j
}; C* f/ U3 z. k
}; h8 ^, M/ w3 R' x
5 T/ K4 ?1 U4 t7 K! Y/ ]0 i/**4 ~, x8 i$ d7 M% O, V
* 堆排序" G4 L* S& y# E
* @param arr 待排数组) a2 n" x; P7 D9 \& C8 R
*/& z% u; E, q; m- P2 c/ F
public static void sort(int arr[]){
; e2 N# q" D: Z3 m buildHeap(arr, arr.length);//先构造大顶堆' J/ ^4 {0 X1 }& d) m
//每次构建堆后将根节点和最后一个节点进行交换; c. W5 [* S9 u! y( I/ n
//然后砍断最后一个节点
# C% w8 d1 Z3 |; w7 E //所以从最后一个节点向前循环/ l6 `1 @; G& C0 n% K# m
for (int i = arr.length - 1; i > 0; i--){
. V7 a. h* R* p" _ swap(arr, 0, i);$ ^8 b8 v4 t: A
heapIfy(arr, i, 0);/ k3 b8 r7 y& Q8 x1 H) q
}% f# [# d- I6 C7 W Z* {7 \( R
}
! z( b/ p4 [+ m7 y; |————————————————# u8 Y+ S! X0 y
版权声明:本文为CSDN博主「qq_34912889」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
) ]: P% A" c* e/ _原文链接:https://blog.csdn.net/qq_34912889/article/details/105690644
+ ?4 B' h. u6 d; I3 @& }
Q' ?2 f& e8 R' B: F9 J
|, N* F2 ?( ` |
zan
|