- 在线时间
- 1630 小时
- 最后登录
- 2024-1-29
- 注册时间
- 2017-5-16
- 听众数
- 82
- 收听数
- 1
- 能力
- 120 分
- 体力
- 566881 点
- 威望
- 12 点
- 阅读权限
- 255
- 积分
- 175287
- 相册
- 1
- 日志
- 0
- 记录
- 0
- 帖子
- 5313
- 主题
- 5273
- 精华
- 3
- 分享
- 0
- 好友
- 163
TA的每日心情 | 开心 2021-8-11 17:59 |
|---|
签到天数: 17 天 [LV.4]偶尔看看III 网络挑战赛参赛者 网络挑战赛参赛者 - 自我介绍
- 本人女,毕业于内蒙古科技大学,担任文职专业,毕业专业英语。
 群组: 2018美赛大象算法课程 群组: 2018美赛护航培训课程 群组: 2019年 数学中国站长建 群组: 2019年数据分析师课程 群组: 2018年大象老师国赛优 |
- m9 W4 c0 G1 \0 R9 h
十大经典排序算法之堆排序(Java语言)2 n& Z! z. ?9 Y8 k
文章目录
: H }3 ]; \: w2 w, @& S5 _ f, U+ P9 T5 p f- F
什么是堆
' m8 p& C: x* ]4 m- y4 Z如何进行堆排序呢& Q" a$ ]' _2 ?7 i# _! k
用数组构建一个堆: @5 @ |0 e8 f3 A: Z8 G
上代码
3 Z) Q% Y4 ~+ D7 r( P8 f什么是堆
1 f; i$ A! \( k1 N2 l* J* A2 e: ~* P9 e
在了解什么是堆之前一定要先了解什么是完全二叉树
/ l* `2 [4 _! K( O) \- @8 L- i看一下百度百科的介绍3 l, ~* e8 D9 q6 L( f! F
8 I5 g+ H! z+ i
若设二叉树的深度为h,除第 h 层外,其它各层 (1~h-1) 的结点数都达到最大个数,第 h 层所有的结点都连续集中在最左边,这就是完全二叉树。
d( X! i5 |; A; x百度百科拗口版性质介绍,能看懂上面的就行,下面的大概看下, Y! O0 R" d5 Q# ~# s. @) z
, y" w: o- y( n! X完全二叉树是由满二叉树而引出来的。对于深度为K的,有n个结点的二叉树,当且仅当其每一个结点都与深度为K的满二叉树中编号从1至n的结点一一对应时称之为完全二叉树。
+ }- R, V$ V% c5 \(1)所有的叶结点都出现在第k层或k-l层(层次最大的两层)3 r1 X2 \( i& c+ s. ?" p- D
(2)对任一结点,如果其右子树的最大层次为L,则其左子树的最大层次为L或L+l。
4 O: D' \8 Q: x! `/ Y; t* ^一棵二叉树至多只有最下面的两层上的结点的度数可以小于2,并且最下层上的结点都集中在该层最左边的若干位置上,则此二叉树成为完全二叉树,并且最下层上的结点都集中在该层最左边的若干位置上,而在最后一层上,右边的若干结点缺失的二叉树,则此二叉树成为完全二叉树。+ v3 k; c1 R$ d* \" j3 B S
那么在了解到什么是完全二叉树之后,我们再来看什么是堆
9 R5 `+ o( v$ e堆有以下两个性质6 q9 E2 t7 r4 W$ `5 ~2 y
. h+ q7 L4 ^ ~1 堆中某个节点的值总是不大于或不小于其父节点的值;; w' Y: B) d3 R8 `
2 堆总是一棵完全二叉树。9 f% l; h" @) ~6 {0 R8 w
其中堆顶就对应二叉树的根
: |2 G5 a) p9 _" j; ]* s* |7 B; M- L$ ]9 k
堆又分为大顶堆和小顶堆,根据堆的第一个性质来进行区分
- u! s+ Q1 `. H( V3 U# s; D: x6 M( e9 o6 E% ]! p' V
当堆中某个节点的值总是大于它的子节点的时候这个堆为大顶堆,反之为小顶堆
% u. A4 U7 c4 K% s9 Z. M' Z! ^3 u如何进行堆排序呢0 H: W+ B. X: s2 e$ U
7 D- T, g* v: ]: S( }0 [8 [1 h
堆排序,其实就是每次构造出一个大顶堆或者小顶堆,然后取出堆顶的值,再将剩下的值重新构造成大顶堆或者小顶堆,最终到堆里的值全部取出来,取出来的数就是排好序的# X$ w4 c/ [' t v) y5 ^' ]
' |2 T( X; \* ~/ `& k" S. i5 H6 C4 b
用数组构建一个堆0 i6 L9 p. v) f5 h" R
' h& j" d* K# s; D6 K" ?因为堆是一颗完全二叉树,所以我们可以用数组来对其进行存储. L. Z, e1 {/ _; s) @
对于用数组存储的二叉树,我们可以用如下方法来定义:2 z1 K4 k$ N! H( \8 O, x+ O' M
假设当前节点的下标为 n
( ~5 G( E7 R! Q5 i# K% j
( a1 r8 y4 R4 z |2 j, \0 W/ [1、那么他的左子节点的下标 2*n + 1
* N5 a" y' j, c% Q3 h4 M) K2、那么他的右子节点的下标 2*n + 21 e" Q2 f3 p6 d8 F+ o0 c: y$ W$ W
3、他左边的节点是 n-1,如果当前节点是第 h 层的最左节点,那么第h-1层的最右节点的下标就是 n-18 z7 F( E8 ]9 W( |2 H( q
4、根据1、2可以推出来n节点的父节点是 (n-1)/2,不管当前节点是父节点的左子节点还是右子节点,都用 (n-1)/2就行了,因为整型数字相除小数点后面的会被截断& t2 w" A. g: z U6 z+ T
那么有了上面四条性质,我们就可以开始动手了8 d8 [ \$ d& C$ {2 Y, x1 H
8 G+ R: K& x8 ?3 |# N1、假设我们要构建的堆是大顶堆,那么根据大顶堆的性质,任意节点都比它的左右子节点要大,所以我们肯定有个heapify方法,该方法调整指定节点和其子节点的位置,并且继续调整被调整的子节点和孙子节点的关系,直到没有调整或者到数的最底层6 J( z+ P' K. @: T
2、然后我们要有构造大顶堆的方法,构造大顶堆就是从最后一个节点的父节点开始调整,接设最后一个节点的父节点是n,那么我们就将n,n-1,n-2 ··· ··· 0,这些节点逐次,从大到小调用heapify方法,这些节点都调整完成后,大顶堆就构造完成了! f; e3 A+ T8 ]2 P% R, ?
3、接下来就开始将堆顶和堆尾互换,并砍断堆尾的操作了,由于互换之前,这是一个符合条件的大顶堆,但是换完只有只有一个堆顶这里不满足了,那么我们重新调整一下堆顶的三个元素就可以,还是调用heapify方法,这个方法会自上而下的重新调整堆,使其成为一个大顶堆7 [) L( c- E; T/ c, M* U8 N
7 A/ v. W3 |( V; z
堆排序的性质
" b' R; ?/ B, p: i) I6 @% N# m1 l
& t6 l, M% x; U& h0 S0 A中文名称 英文名称 平均时间复杂度 最坏时间复杂度 最好时间复杂度 空间复杂度 稳定性
3 O3 `5 Q6 m1 d A: r堆排序 Heap n*logn n*logn n*logn 1 不稳定
- n) j) x& |+ R, B# l上代码' ?- L# q$ w0 v
, ^$ e# P, I) ~ S
/**5 N6 h H' Z: K8 h/ E
* 交换第n和m个元素
+ m" `. U$ Q% _- \ */+ T1 `) ?' F; q N
private static void swap(int arr[], int n, int m){
5 c9 P+ S1 _+ Z% _( u int temp = arr[n];
6 K* R! y7 o/ {9 A! C arr[n] = arr[m];9 S2 @* d# l2 t) v
arr[m] = temp;
: \% r; C/ X# Y9 T}( w4 y$ V n+ ]. V- P9 u
% `: b% A$ R0 Y+ p) B5 z- s+ g/**
5 l3 l9 F0 ^) X' |5 d$ d2 m+ m * 调整指定节点和其子节点 w. f7 I* J. Y$ L+ f# O
* @param tree 整棵树" g9 S/ K0 v3 {. K
* @param n 数组长度,树的元素个数$ q- v- c/ y' T: {- p/ W7 Y
* @param i 要调整的节点的下标1 ^& F% G6 m: ]: r
*/
) B# ^0 k& X% X+ L6 Pprivate static void heapIfy(int tree[], int n, int i){
1 i/ E5 l; K+ j/ E+ u# y if(i >= n){+ c: ? z1 c0 U& u; ]7 B7 e
return;. ~9 J* @* v, e
}
4 Z* l/ X9 B' t; N int c1 = 2 * i + 1;//左子节点的下标3 K1 [# b. k0 I; k, K# \
int c2 = 2 * i + 2;//右子节点的下标8 R8 a: V9 X5 S* }$ a
int max = i;//假设父节点是最大的8 M" P+ S& a; V9 _+ T$ [9 ^4 I. K% e
//找出最大值的下下标/ D% r. a. h, |
if(c1 < n && tree[c1] > tree[max]){: \7 ]' d" ~- K5 R
max = c1;
4 m! A9 c: y5 s+ [+ k }7 l. p4 Y& Q! q
if(c2 < n && tree[c2] > tree[max]){" X1 E* L9 F& y3 d8 E0 z9 F) V. B
max = c2;, W u/ a" H, X5 |* J
}
9 A# \- v! j- c1 C" X' B if(max != i){//如果最大值不是父节点,需要做换位置操作
7 [5 u+ t5 N' f- M' y swap(tree, max, i);
8 O$ l9 f* U* v+ l q( _0 P: O //此时,i节点被换成最大值了,符合大顶堆的性质! g! }3 @& e- N2 @& N- ^/ g2 x
//但是换到下面的节点不能保证比他的两个子节点都要大/ E( i; k& R' d4 ?+ v
//所以被换位置的节点继续调整$ D! R" ?/ k& s% _& d5 j' P
heapIfy(tree, n, max);2 j/ g, Z. x( R% {. g/ P, M8 p
}0 [! n, ?, R6 i+ L
}8 w- z5 B0 m' \3 P* E$ p. |
! U! `8 i2 N* T& D4 z2 o
/**6 u; S, U6 w- X7 k
* 完整构建大顶堆% D: g2 b; v1 E! r9 f0 V
* @param arr 用于构建堆的数组& D' [9 x; g4 E" g2 _* d7 T1 _1 o
* @param n 堆的最后一个节点的下标8 ]: M4 }( W6 ?" H/ j0 a3 G
*/0 `- Q. @$ i% C8 B/ J
private static void buildHeap(int arr[],int n){
% a3 Q1 b- Y7 D) \ int lastNode = n - 1;5 Y$ B+ p8 s3 N6 J. ]* Z4 M2 L' ?
int parent = (lastNode - 1) / 2;" F4 p% \; n, c8 N. r% x3 r
for (int i = parent; i >= 0; i--){
8 O, ?+ D" K1 H( M! u" g1 z" u heapIfy(arr, n, i);
a7 r7 A P, d" @ }7 r; D) G5 ]' m% y v) A; x
}
% Y# |0 b' \: u$ i) H X5 J% c* @- e
/**3 o* G' T7 G* I! i
* 堆排序
3 D' k1 I6 B5 e; L/ N3 a8 X" \ * @param arr 待排数组
* X' ]% R( S T- N6 G */
2 R1 A) ?1 p$ j+ \6 u% bpublic static void sort(int arr[]){
+ p s) F% h$ Y2 w buildHeap(arr, arr.length);//先构造大顶堆
% H! Z0 w' e) O2 [' d, E% x //每次构建堆后将根节点和最后一个节点进行交换
3 u6 O6 P. z* e* }0 |8 `) s //然后砍断最后一个节点) C* \ d0 l1 f5 N! H
//所以从最后一个节点向前循环
" `. G r( T7 s* ]/ m for (int i = arr.length - 1; i > 0; i--){! A0 H+ g2 g) \% h2 |
swap(arr, 0, i);
! B* A" l x/ z2 X heapIfy(arr, i, 0);% s, B3 b* C6 H
}
( B" B$ P8 P4 N6 G( x" d! p9 r}
8 p/ n* U+ v' c' H! {; ^————————————————
) G! {2 M/ F- F8 z$ S- Y0 c: Y' B版权声明:本文为CSDN博主「qq_34912889」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
! P) i6 R) @9 y9 v* u原文链接:https://blog.csdn.net/qq_34912889/article/details/105690644
( z3 W9 L9 l/ L+ e |( F5 e$ B k6 U0 F; ]* {9 S" T
8 H& Z6 M3 |! q, K |
zan
|