- 在线时间
- 1630 小时
- 最后登录
- 2024-1-29
- 注册时间
- 2017-5-16
- 听众数
- 82
- 收听数
- 1
- 能力
- 120 分
- 体力
- 556956 点
- 威望
- 12 点
- 阅读权限
- 255
- 积分
- 172460
- 相册
- 1
- 日志
- 0
- 记录
- 0
- 帖子
- 5313
- 主题
- 5273
- 精华
- 18
- 分享
- 0
- 好友
- 163
TA的每日心情 | 开心 2021-8-11 17:59 |
---|
签到天数: 17 天 [LV.4]偶尔看看III 网络挑战赛参赛者 网络挑战赛参赛者 - 自我介绍
- 本人女,毕业于内蒙古科技大学,担任文职专业,毕业专业英语。
 群组: 2018美赛大象算法课程 群组: 2018美赛护航培训课程 群组: 2019年 数学中国站长建 群组: 2019年数据分析师课程 群组: 2018年大象老师国赛优 |
U* \8 \6 S. Q& A' k十大经典排序算法之堆排序(Java语言)
" l, s1 L3 e5 u& ^( o6 |文章目录- }! B1 D6 z& |) R
* Z; o: f% U/ I( I
什么是堆
' w; c* l2 }7 q" z4 `. n" @如何进行堆排序呢
+ }( @& }8 T, \& P; C用数组构建一个堆/ ^" K. o' a/ g( p" N' ^
上代码' Z4 B3 S& d1 C. w, ^! j
什么是堆- B4 G# j9 F$ l$ o* N9 y' J2 w3 v
, { e6 j h2 D; N在了解什么是堆之前一定要先了解什么是完全二叉树
a2 P. m4 t- i, ~看一下百度百科的介绍8 }, |5 @0 V* l9 w4 C* c0 T+ Z) H
# X2 ~! t8 @; g. L" P& h1 o3 {若设二叉树的深度为h,除第 h 层外,其它各层 (1~h-1) 的结点数都达到最大个数,第 h 层所有的结点都连续集中在最左边,这就是完全二叉树。
" P! u3 l- t) Y百度百科拗口版性质介绍,能看懂上面的就行,下面的大概看下
7 y9 t2 R( }+ A. Q' M+ ] A; ] l* o z. v3 }4 s+ _7 {% h
完全二叉树是由满二叉树而引出来的。对于深度为K的,有n个结点的二叉树,当且仅当其每一个结点都与深度为K的满二叉树中编号从1至n的结点一一对应时称之为完全二叉树。
6 e$ S3 w; @1 z: p(1)所有的叶结点都出现在第k层或k-l层(层次最大的两层)) B& h6 v2 v! p: K' q: X
(2)对任一结点,如果其右子树的最大层次为L,则其左子树的最大层次为L或L+l。3 U/ d+ d8 s6 \! z
一棵二叉树至多只有最下面的两层上的结点的度数可以小于2,并且最下层上的结点都集中在该层最左边的若干位置上,则此二叉树成为完全二叉树,并且最下层上的结点都集中在该层最左边的若干位置上,而在最后一层上,右边的若干结点缺失的二叉树,则此二叉树成为完全二叉树。$ x5 e/ a/ O, p
那么在了解到什么是完全二叉树之后,我们再来看什么是堆
! B# [& T( r( Y$ B3 ~7 v! ?堆有以下两个性质
1 U6 K, ^ O* W. d/ L
K: g& s; F. H! L1 堆中某个节点的值总是不大于或不小于其父节点的值;) E- b- r0 D7 e0 x& |+ A( Z
2 堆总是一棵完全二叉树。
4 F! w5 ]' x, E; n其中堆顶就对应二叉树的根
* N/ d8 p0 L2 |
: Q6 ~8 f9 m5 R' g) |, ?堆又分为大顶堆和小顶堆,根据堆的第一个性质来进行区分
2 ~* I; O1 x* y1 E' s5 N6 q, l; L$ Q, [) m9 w) d7 B
当堆中某个节点的值总是大于它的子节点的时候这个堆为大顶堆,反之为小顶堆. ` m3 ?% c0 J, g7 A2 }
如何进行堆排序呢4 H; `" E) ~3 v+ P6 t' L
, k2 x0 h5 Y/ Q/ f' A5 U
堆排序,其实就是每次构造出一个大顶堆或者小顶堆,然后取出堆顶的值,再将剩下的值重新构造成大顶堆或者小顶堆,最终到堆里的值全部取出来,取出来的数就是排好序的! W$ [* \2 m! u9 a( ?
4 z% ~: z2 ~. K6 r, B+ S4 K% q0 U
用数组构建一个堆
$ j+ {0 M [' P J* M& g- s! A3 s' ]& W& M6 ^' v
因为堆是一颗完全二叉树,所以我们可以用数组来对其进行存储
9 ]( |8 _/ S3 C9 I" o- B对于用数组存储的二叉树,我们可以用如下方法来定义:
; ]: X8 I& T4 g2 C: j0 t3 Z0 E假设当前节点的下标为 n
! s: v# W# Y* s6 u9 n' ?% a
4 o0 |% R; I4 j5 d1、那么他的左子节点的下标 2*n + 1
& T) l* a) c0 H2、那么他的右子节点的下标 2*n + 2
: i2 h4 a$ P( d) {0 c3、他左边的节点是 n-1,如果当前节点是第 h 层的最左节点,那么第h-1层的最右节点的下标就是 n-14 v6 o- k( f! q
4、根据1、2可以推出来n节点的父节点是 (n-1)/2,不管当前节点是父节点的左子节点还是右子节点,都用 (n-1)/2就行了,因为整型数字相除小数点后面的会被截断
' r. N h2 T% V那么有了上面四条性质,我们就可以开始动手了! U* T0 _- |( d
4 I" O# R9 a6 o& v: a; f; [
1、假设我们要构建的堆是大顶堆,那么根据大顶堆的性质,任意节点都比它的左右子节点要大,所以我们肯定有个heapify方法,该方法调整指定节点和其子节点的位置,并且继续调整被调整的子节点和孙子节点的关系,直到没有调整或者到数的最底层$ n; d' c/ ^) P
2、然后我们要有构造大顶堆的方法,构造大顶堆就是从最后一个节点的父节点开始调整,接设最后一个节点的父节点是n,那么我们就将n,n-1,n-2 ··· ··· 0,这些节点逐次,从大到小调用heapify方法,这些节点都调整完成后,大顶堆就构造完成了
( B* ?. w2 L% m5 D# V: w3、接下来就开始将堆顶和堆尾互换,并砍断堆尾的操作了,由于互换之前,这是一个符合条件的大顶堆,但是换完只有只有一个堆顶这里不满足了,那么我们重新调整一下堆顶的三个元素就可以,还是调用heapify方法,这个方法会自上而下的重新调整堆,使其成为一个大顶堆
# u7 [1 ?& u9 s" m
' |1 `3 i/ U. g- p* ^+ }# Y9 t- z$ h堆排序的性质
9 f, Q7 E3 R! Z7 `6 w {& l1 F6 h8 q0 B! e
中文名称 英文名称 平均时间复杂度 最坏时间复杂度 最好时间复杂度 空间复杂度 稳定性
2 I2 v8 v# z) U6 i/ N' ?2 p* e堆排序 Heap n*logn n*logn n*logn 1 不稳定3 z9 e. a* U5 z
上代码4 l' n0 L0 j; f: Q5 H+ O0 N' J4 N" K
/ n2 E% G$ m5 \( _6 N2 @& T) q/**
Q5 O. }- D# A5 p; A/ ~ * 交换第n和m个元素
: w. R" d% M1 l9 ?# Q */# b8 q% t+ h1 z. P( S% A1 F2 Z
private static void swap(int arr[], int n, int m){. i* X( P0 b1 Q
int temp = arr[n];9 \( j; p3 a3 j( @0 e, S1 k( C D
arr[n] = arr[m];$ O* O' @2 Y7 }& F# d: b/ ?( a
arr[m] = temp;& [" Z( ?, d" ]5 r# s
}
* M$ A$ A7 D) ~2 t0 P0 x# v) T# @
( U' u- N, W! a* y1 I/**: e0 O% m4 a; `6 N
* 调整指定节点和其子节点7 N$ w* G" D3 w9 T1 M6 ~$ `0 E
* @param tree 整棵树
5 D" J2 A% I! M( m# G9 B4 ? * @param n 数组长度,树的元素个数8 ^1 d# j$ p f- \
* @param i 要调整的节点的下标* }% @) F- s2 T4 }- T+ v8 j- O
*/* ]5 b" g" V$ {( F
private static void heapIfy(int tree[], int n, int i){
4 A, I' F$ X. O0 Y( Z- q8 f0 N# g if(i >= n){
2 l1 D' X [: z9 |7 S return;
) U$ y$ y: h8 ^+ |9 h. Y; X1 ?) B }
, U- D/ V# Y, d0 B int c1 = 2 * i + 1;//左子节点的下标/ ~' o! o8 N E# G* m8 c
int c2 = 2 * i + 2;//右子节点的下标
2 `5 U& F) Y4 o: V+ d int max = i;//假设父节点是最大的
" [7 Y: C" _; z3 J: F% T B; Z //找出最大值的下下标" _' j/ j! q" N, \
if(c1 < n && tree[c1] > tree[max]){
0 u: o2 s3 o2 V4 \4 {) U max = c1;. k8 g) E" X0 S) [1 L4 K8 h
}6 I' n! F' F6 @$ X$ L, V
if(c2 < n && tree[c2] > tree[max]){9 U) H7 I3 e4 l' X6 }
max = c2;
, X9 Y# S; ]- p) y$ v1 I! q- I }+ R5 s! o8 l! B( q, K
if(max != i){//如果最大值不是父节点,需要做换位置操作
- M) p8 H) B- B4 I2 {8 E4 H swap(tree, max, i);9 q+ J! y3 x2 G2 B, L
//此时,i节点被换成最大值了,符合大顶堆的性质1 B* v; Q3 h5 x* _# I, X
//但是换到下面的节点不能保证比他的两个子节点都要大
% H% W/ f+ M2 B% [% ]4 M6 q //所以被换位置的节点继续调整; e8 S6 M( u# C" I9 o8 t9 L
heapIfy(tree, n, max);
" F; R0 p# R+ n }# A0 O) A+ n1 X3 P$ i x/ ?
}
% o: o; g6 [/ T; \
8 ^& Q5 |" z$ _# Q/ ]: A/**
2 _0 y' g3 y& V8 G8 U4 q- [ * 完整构建大顶堆
% c- Q' p5 \ }+ E; t9 u * @param arr 用于构建堆的数组7 f r8 E# y! }( Y* _: g
* @param n 堆的最后一个节点的下标5 U% ]8 ~& |$ X) W
*/
/ Q& A5 X* [, `8 vprivate static void buildHeap(int arr[],int n){; z2 c, g( z7 {! y7 p
int lastNode = n - 1;
1 l0 g+ X N6 p9 h int parent = (lastNode - 1) / 2;
$ {1 o. g6 X# v& u; I for (int i = parent; i >= 0; i--){
# ]& ]* d' }, [, g5 O# t+ R heapIfy(arr, n, i);
0 e3 r4 ?5 y5 _9 a' I% ^ }
1 s2 r/ t# k2 H5 v; Y1 b* k}
3 C) u. g. O/ n8 H! s
2 I4 ]" U- [) e1 F/**
5 a; [; [) M' F0 J: Q" K * 堆排序4 x& z4 T# [; ]0 P" S G* }
* @param arr 待排数组
8 q0 T: l( h Y6 a */, Z* B: Y: p R* b' v
public static void sort(int arr[]){
7 Q! J; X, r$ N3 j# W3 S( r buildHeap(arr, arr.length);//先构造大顶堆, s) H/ O$ n z6 N. U% Q
//每次构建堆后将根节点和最后一个节点进行交换6 R6 b5 e8 v5 a- D5 q
//然后砍断最后一个节点" {! M$ i" L. H; {$ @" h
//所以从最后一个节点向前循环
; T4 v- @7 G, G) P: |, E* S* r for (int i = arr.length - 1; i > 0; i--){1 k B* D: r1 l( ~4 [1 C
swap(arr, 0, i);
" z! m" c6 C: X% x) R1 h6 h heapIfy(arr, i, 0);
, Q: N# g! `6 j' @& q/ \6 T# y }9 k2 S. w% Q* y1 J7 \5 ^/ l
}
8 S {% u+ k8 \1 Y————————————————
|, N4 x0 j; v A: j5 k! f5 ~( X版权声明:本文为CSDN博主「qq_34912889」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。" m. B5 H) N! S$ D+ i; u% x3 p- D6 ]
原文链接:https://blog.csdn.net/qq_34912889/article/details/105690644
5 o& C! g. p" w' ?% L
* ]. O' k) _8 {' k$ [; ?7 w) J4 R8 R8 L; k
|
zan
|