- 在线时间
- 1630 小时
- 最后登录
- 2024-1-29
- 注册时间
- 2017-5-16
- 听众数
- 82
- 收听数
- 1
- 能力
- 120 分
- 体力
- 565560 点
- 威望
- 12 点
- 阅读权限
- 255
- 积分
- 174891
- 相册
- 1
- 日志
- 0
- 记录
- 0
- 帖子
- 5313
- 主题
- 5273
- 精华
- 3
- 分享
- 0
- 好友
- 163
TA的每日心情 | 开心 2021-8-11 17:59 |
|---|
签到天数: 17 天 [LV.4]偶尔看看III 网络挑战赛参赛者 网络挑战赛参赛者 - 自我介绍
- 本人女,毕业于内蒙古科技大学,担任文职专业,毕业专业英语。
 群组: 2018美赛大象算法课程 群组: 2018美赛护航培训课程 群组: 2019年 数学中国站长建 群组: 2019年数据分析师课程 群组: 2018年大象老师国赛优 |
% X2 `% [9 {6 _9 Q
十大经典排序算法之堆排序(Java语言)
u/ ~& |- f% s2 @文章目录- S+ T( f: q! w/ s/ R- `, D! P, [
8 P i7 V5 o5 ~) _1 A9 R
什么是堆' K0 R3 b8 g+ H; K6 }
如何进行堆排序呢! w, n' i5 i$ k4 Y* c
用数组构建一个堆( q5 n2 U% A9 f7 y
上代码7 Z! K/ l* Y4 P5 I" \3 @1 S
什么是堆8 V* j4 ?6 P9 b( j
3 a+ Y4 c4 i8 i7 A: i+ X/ m' I在了解什么是堆之前一定要先了解什么是完全二叉树
7 n- p0 r) D& X, v" i" a看一下百度百科的介绍
) ~/ M. i0 @6 f X- E
9 @, ~' w5 e' [6 Y+ n7 O若设二叉树的深度为h,除第 h 层外,其它各层 (1~h-1) 的结点数都达到最大个数,第 h 层所有的结点都连续集中在最左边,这就是完全二叉树。3 t7 b4 R0 k1 Q- ` c/ r
百度百科拗口版性质介绍,能看懂上面的就行,下面的大概看下
! {+ S- ?: {& Z7 _9 b( R) }9 E) N2 f
完全二叉树是由满二叉树而引出来的。对于深度为K的,有n个结点的二叉树,当且仅当其每一个结点都与深度为K的满二叉树中编号从1至n的结点一一对应时称之为完全二叉树。* k) f( @: k- H8 a7 K7 \
(1)所有的叶结点都出现在第k层或k-l层(层次最大的两层)0 U; h( |5 R4 Z. g, A: A7 {% x8 [* E
(2)对任一结点,如果其右子树的最大层次为L,则其左子树的最大层次为L或L+l。
, D& b6 V. i! ]' S一棵二叉树至多只有最下面的两层上的结点的度数可以小于2,并且最下层上的结点都集中在该层最左边的若干位置上,则此二叉树成为完全二叉树,并且最下层上的结点都集中在该层最左边的若干位置上,而在最后一层上,右边的若干结点缺失的二叉树,则此二叉树成为完全二叉树。
& s: q8 ?0 z/ J: ~4 ?1 B- L那么在了解到什么是完全二叉树之后,我们再来看什么是堆' |" F/ p0 r+ n$ ]/ k3 e0 A# y0 L7 ~
堆有以下两个性质
) v. t- B" L7 S6 D8 A% `0 a& r( @; Z0 U
1 堆中某个节点的值总是不大于或不小于其父节点的值;8 S5 |; t& X1 W' r8 F0 i2 @
2 堆总是一棵完全二叉树。0 c1 F5 @% P# g L+ u' c; h
其中堆顶就对应二叉树的根
7 V( b4 h( E& @9 g' a. Q5 T
' D) z B: L2 T. L堆又分为大顶堆和小顶堆,根据堆的第一个性质来进行区分
& H7 V8 p3 m$ T0 j( C$ g" j& v) X4 `2 r8 F- H/ Q
当堆中某个节点的值总是大于它的子节点的时候这个堆为大顶堆,反之为小顶堆
3 j9 {5 b' j) m: w2 L! R如何进行堆排序呢, H5 W* t5 D0 R3 f: \
+ b5 [/ O# p3 C堆排序,其实就是每次构造出一个大顶堆或者小顶堆,然后取出堆顶的值,再将剩下的值重新构造成大顶堆或者小顶堆,最终到堆里的值全部取出来,取出来的数就是排好序的) X; y& [: J7 v
& D, a' F) }% Z9 G4 M L7 \6 B用数组构建一个堆
0 M; m. \5 p' d' F6 W0 c4 u, o$ U5 c1 Z# g
因为堆是一颗完全二叉树,所以我们可以用数组来对其进行存储0 m1 l8 l. o! z; n8 [
对于用数组存储的二叉树,我们可以用如下方法来定义:: k0 _' n9 _' t% k3 _6 z
假设当前节点的下标为 n; C, E% T% b* W u6 R) S+ Q; s% o
) U5 F1 l! h+ b1 r( q9 H% r. L
1、那么他的左子节点的下标 2*n + 1
; N' @" z7 q7 z, ^: `8 q! p2、那么他的右子节点的下标 2*n + 2( l- H# @1 ]" c( o# e4 `% \* J( H
3、他左边的节点是 n-1,如果当前节点是第 h 层的最左节点,那么第h-1层的最右节点的下标就是 n-1
. g5 Y! L( y8 U9 }6 r4、根据1、2可以推出来n节点的父节点是 (n-1)/2,不管当前节点是父节点的左子节点还是右子节点,都用 (n-1)/2就行了,因为整型数字相除小数点后面的会被截断' A5 i3 |; W8 D: Q* L
那么有了上面四条性质,我们就可以开始动手了
/ I- [( G. M3 Q( {2 L8 `* h2 o/ Q, \2 b& |
1、假设我们要构建的堆是大顶堆,那么根据大顶堆的性质,任意节点都比它的左右子节点要大,所以我们肯定有个heapify方法,该方法调整指定节点和其子节点的位置,并且继续调整被调整的子节点和孙子节点的关系,直到没有调整或者到数的最底层
* s6 a, w8 B5 E0 j3 b+ \) @9 w* Y2、然后我们要有构造大顶堆的方法,构造大顶堆就是从最后一个节点的父节点开始调整,接设最后一个节点的父节点是n,那么我们就将n,n-1,n-2 ··· ··· 0,这些节点逐次,从大到小调用heapify方法,这些节点都调整完成后,大顶堆就构造完成了# x/ c3 v6 Z) P3 R& \ `
3、接下来就开始将堆顶和堆尾互换,并砍断堆尾的操作了,由于互换之前,这是一个符合条件的大顶堆,但是换完只有只有一个堆顶这里不满足了,那么我们重新调整一下堆顶的三个元素就可以,还是调用heapify方法,这个方法会自上而下的重新调整堆,使其成为一个大顶堆" g) H6 G4 Y7 J( w6 z, Z
6 u: b& g9 h0 G( W/ R! W堆排序的性质7 Y$ I# ?: P0 m* X N% s9 t9 P
+ x% L y# ?3 Q5 J$ c8 S2 e
中文名称 英文名称 平均时间复杂度 最坏时间复杂度 最好时间复杂度 空间复杂度 稳定性
& r) G u4 a/ y堆排序 Heap n*logn n*logn n*logn 1 不稳定2 B/ ~# Q; C) S) ` a! |/ G
上代码
7 R8 {3 \9 {2 \7 v# T& O6 g G: `+ J$ q! c
/**2 b1 G y g/ _( o1 O4 X9 M
* 交换第n和m个元素- r4 u3 _5 a( I$ o; n
*/
, ^" k Z3 q2 I) ~% c2 d0 m" Wprivate static void swap(int arr[], int n, int m){
- [5 L Y8 z0 \9 n* a5 W; E# Z int temp = arr[n];7 N5 D5 A8 [5 Q7 ]% f4 l/ N K
arr[n] = arr[m];
6 M3 j' \. R' |* _) B arr[m] = temp;
- z+ [/ E3 Y$ w* O" v}
# j) w4 Q/ r6 y% z- _3 V( x& l/ X: }' z* \
/**
4 b8 ?! ?& i# b ~# J' u+ l * 调整指定节点和其子节点0 T1 I9 k0 h, @" O( w1 g! K* p
* @param tree 整棵树& j }$ N+ k+ B; M
* @param n 数组长度,树的元素个数1 W) `: R y1 e7 o
* @param i 要调整的节点的下标/ j, n, _7 A. B
*/4 ?" x0 j5 F3 l Z- f+ Q
private static void heapIfy(int tree[], int n, int i){ u$ A& ?7 N9 a" Y
if(i >= n){& l6 I9 j) b; Z, P% _4 p
return;
' M; z2 L2 ?( j; j, F+ x }
0 D8 Y- g# U ~% d int c1 = 2 * i + 1;//左子节点的下标: ?1 C" t9 [. F! B) _" B6 e' z# c
int c2 = 2 * i + 2;//右子节点的下标( t) K. W: b0 c8 o; ~8 ~$ J
int max = i;//假设父节点是最大的+ u# L' g& u/ ~
//找出最大值的下下标 D9 [7 W# M& M2 ?2 ]+ ?0 a0 A
if(c1 < n && tree[c1] > tree[max]){
' D: [, J* E& W2 h max = c1;
; m$ E2 D @: Q z }8 O& o8 p% ]5 _' h2 c) D- j
if(c2 < n && tree[c2] > tree[max]){
0 H; u8 r5 S4 U2 H max = c2;& ^7 S6 i( h5 Z8 @ z# M( g# L
}4 i: I3 v$ A3 O0 C u
if(max != i){//如果最大值不是父节点,需要做换位置操作
& {! A$ A: `2 w# ]6 V swap(tree, max, i);9 v* d7 d. i8 @1 P
//此时,i节点被换成最大值了,符合大顶堆的性质5 ]9 K1 v$ K( \. s- v
//但是换到下面的节点不能保证比他的两个子节点都要大
6 E, x: ~ I! f' X4 H, p6 S //所以被换位置的节点继续调整& s2 I& p4 E$ l0 B: X' B
heapIfy(tree, n, max);
" @/ G: G) \$ a, o& `7 P3 C; z- D }0 c$ D9 o* V3 d
}- Q% J3 H7 x0 [3 ?# H/ k/ D ]
1 c# s7 _/ X$ n* L0 w/**$ ?# U6 s$ X7 o; b1 }
* 完整构建大顶堆7 y/ I4 o7 ?! \$ b
* @param arr 用于构建堆的数组' ]0 e7 K7 B5 Z' L- \/ e
* @param n 堆的最后一个节点的下标
$ R6 E# G8 u1 ^; V( H, s */5 s8 T5 S2 `% }1 l
private static void buildHeap(int arr[],int n){
$ f! q" _+ X# G$ I: e% ~; a int lastNode = n - 1;
3 t0 z& v) `9 g int parent = (lastNode - 1) / 2;
$ {0 o8 i" x* { for (int i = parent; i >= 0; i--){; O0 e% Q c3 p4 s" _4 I
heapIfy(arr, n, i);
- ?$ I/ o0 k5 @' { } }3 G; U0 ~+ P+ _
}
2 e, d7 D" H; X
6 w) T p5 w0 i; A# q [& Z/**
' D& i( N) c) ?% m/ j * 堆排序/ y. S5 W( I- X
* @param arr 待排数组0 |) }- L- o1 J( f3 m
*/- _2 M& z/ c4 t3 D( }/ C
public static void sort(int arr[]){; s( Y7 A( @9 G0 m
buildHeap(arr, arr.length);//先构造大顶堆
2 t4 N5 L& x5 T5 G //每次构建堆后将根节点和最后一个节点进行交换
' D A! m. x. _" Q! D //然后砍断最后一个节点
' q* b% l. P+ z, t% h/ ^' S y //所以从最后一个节点向前循环1 B" a1 ?! e! a: m4 Y
for (int i = arr.length - 1; i > 0; i--){
' @# W1 M2 u5 [' L swap(arr, 0, i);
# l8 ?! o- l0 h3 c heapIfy(arr, i, 0);
* S' V0 W$ a5 [) e p4 ` }
( r, `& b2 K2 F8 {" a}
) D+ K7 N n, u————————————————3 @8 |. ^( ]7 F' V$ k9 j
版权声明:本文为CSDN博主「qq_34912889」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。8 m z- C; M* Q; Q& ~, s# l. }
原文链接:https://blog.csdn.net/qq_34912889/article/details/105690644
, {$ @9 ~2 H7 [5 _1 J% t) u+ d( k1 O+ B) i! |0 O3 k" d
" h9 ?& O$ v, A |
zan
|