博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
堆排序-heapsort
阅读量:5258 次
发布时间:2019-06-14

本文共 3194 字,大约阅读时间需要 10 分钟。

什么是堆?"堆"这个词最初是在堆排序中提出的,但后来就逐渐指"废料收集存储区",当然这里不是指"废料收集存储区"。堆数据结构是一种数组对象,由于一棵完全二叉树可以用一组地址连续的存储单元依次自上而下、自左至右存储,故堆可以被视为一棵完全二叉树,如下图:

2011070111124177.jpg

圆圈中的数字表示树中每个节点的值,节点上方的数字表示对应的数组下标。

一个堆的数组A,用length[A]表述数组中的元素个数,heap-size[A]表示本身存放在A中的堆的元素个数,很明显heap-size[A]<=length[A]。

树的根为A[1],给定某个节点的下标i,很容易计算出其父节点PARENT(i)、左孩子LEFT(i)、右孩子RIGHT(i)的下标:
PARENT(i) --- i/2  LEFT(i) --- 2i  RIGHT(i) --- 2i+1

二叉堆有两种:最大堆和最小堆。最大堆满足以下条件:除了根节点以外的每个节点i,有A[PARENT(i)]>=A[i]。即某个节点的值至多和父节点的值一样大,也就是说,在以节点i为根节点的子树中,其子节点的值都不大于该节点的值,由此可得出结论,最大堆根节点的值即是数组A的最大值。最小堆的概念正相反。

堆排序算法使用的是最大堆。下面介绍几个堆排序使用的基本过程:

  • max_heapify过程,运行时间为O(lg n),它是保持最大堆性质的关键 
  • build_max_heap过程,线性时间运行,在无序的数组基础上构造最大堆
  • heapsort过程,运行时间为O(lg n),对一个数组原地进行排序

保持堆的性质 max_heapify算法如下

  max_heapify(A,i) 
    l ← LEFT(i)
    r ← RIGHT(i) 
    if l ≤ heap-size[A] and A[l] > A[i]
      then largest ← l
      else largest ← i
    if r ≤ heap-size[A] and A[r] > A[largest]
      then largest ← r 
    if i ≠ largest
      then exchange A[i] <-> A[largest] 
        max_heapify(A,largest) 

如上述算法描述,首先在数组元素A[i],其左孩子为A[LEFT(i)],右孩子为A[RIGHT(i)]中找到最大的那个,将其下标值存储到变量largest中。如果A[i]已经是最大值,则算法结束,否则A[i]与A[largest]交换,从而使节点i及其子节点满足最大堆的性质。此时,以largest节点为根的子树可能违反最大堆的性质,所以需要对该子树递归调用max_heapify。下图展示了这个过程:

2011070114335377.jpg

该图展示的是max_heapify(A,2)的过程,读者可参考算法自行理解该过程。

建堆 build_max_heap算法如下

  build_max_heap(A)
    heap-size[A] ← length[A]
    for i ← length[A]/2 downto 1
      do max_heapify(A,i) 

当用数组表示存储了n个元素的堆时,可以证明叶子的下标是n/2+1,n/2+2,...,n。

假设第i个节点是堆中最后一个拥有叶子的节点,则它的节点必定是其左孩子(根据完全二叉树的定义可得) ,则LEFT(i)=2i=n,即其左孩子在数组里的存储位置为n,可得i=n/2,所以从第i+1开始的节点没有子节点,即n/2+1,n/2+2,...,n存储的节点是叶子。
所以build_max_heap算法从第length[A]/2个节点往前开始调用max_heapify来建立最大堆,无需在叶子节点上调用max_heapify。下图是此过程的展示:

2011070115123979.jpg

堆排序 heapsort算法如下

  heapsort(A)
    build_max_heap(A)
    for i ← length[A] downto 2
      do exchange A[1] <-> A[i] 
        heap-size[A] ← heap-size[A] - 1
        max_heapify(A,1) 

首先,将输入数组A构造成最大堆,因为数组中最大元素在根A[1],则交换A[1]和A[n]来达到最终正确的位置,此时数组元素最大值为A[n]。现在将A[n]从数组中去掉,可以很容易将A[1..n-1]构造成最大堆。原来的根的子女仍是最大堆,但新的根元素可能违反了最大堆性质,这是调用max_heapify(A,1)就可以保持最大堆性质,在A[1..n-1]中构造最大堆。不断重复这个过程,直到堆的大小降到2。

下面给出具体C语言实现代码:

 
1
void
swap(
int
*
a,
int
*
b)
2
{
3
int
temp
=
*
a;
4
*
a
=
*
b;
5
*
b
=
temp;
6
}
7
8
 
void
max_heapify(
int
*
arr,
int
i,
int
size)
9
{
10
int
lt
=
2
*
i
+
1
;
//
左孩子
11
int
rt
=
2
*
i
+
2
;
//
右孩子
12
int
large;
13
14
if
(lt
<=
size
-
1
&&
arr[lt]
>
arr[i])
15
large
=
lt;
16
else
17
large
=
i;
18
if
(rt
<=
size
-
1
&&
arr[rt]
>
arr[large])
19
large
=
rt;
20
21
if
(large
!=
i)
22
{
23
swap(
&
arr[i],
&
arr[large]);
24
max_heapify(arr,large,size);
25
}
26
}
27
28
void
build_max_heap(
int
*
arr,
int
size)
29
{
30
int
i;
31
for
(i
=
size
/
2
;i
>=
0
;i
--
)
32
{
33
max_heapify(arr,i,size);
34
}
35
}
36
37
void
heapsort(
int
*
arr,
int
size)
38
{
39
int
i,len;
40
len
=
size;
41
build_max_heap(arr,size);
42
43
for
(i
=
size;i
>=
1
;i
--
)
44
{
45
swap(
&
arr[
0
],
&
arr[i
-
1
]);
46
len
--
;
47
max_heapify(arr,
0
,len);
48
}
49
}
50
51
int
main(
void
)
52
{
53
int
arr[]
=
{
4
,
1
,
3
,
2
,
16
,
9
,
10
,
14
,
8
,
7
};
54
int
len
=
sizeof
(arr)
/
sizeof
(arr[
0
]);
55
heapsort(arr,len);
56
int
i
=
0
;
57
for
(;i
<
len;i
++
)
58
{
59
printf(
"
%d
"
,arr[i]);
60
}
61
return
0
;
62
}

end

转载于:https://www.cnblogs.com/ace10/archive/2011/07/01/2095296.html

你可能感兴趣的文章
纸上谈兵: 树, 二叉树, 二叉搜索树[转]
查看>>
第一篇随笔
查看>>
Java中Date类型的工具类
查看>>
WebStorm快捷键收集
查看>>
testng,soket write error错误
查看>>
javaScript中string对象的方法
查看>>
跑一段代码遍历所有汉字
查看>>
生产者/消费者模式(二)
查看>>
Leetcode 226 Invert Binary Tree
查看>>
JAVA-初步认识-第十三章-多线程(卖票示例)
查看>>
[译]转译器: 今日大不同
查看>>
HBase基础介绍
查看>>
我是如何指导团队(软件开发团队)新人的
查看>>
Vue项目实战《硅谷外卖》
查看>>
Base64编码图片存取与前台显示
查看>>
关于弹性空间
查看>>
实用的 Node.js 教程,工具和资源
查看>>
20幅妙不可言的光涂鸦摄影作品
查看>>
标记化结构初始化语法(C语言)
查看>>
使用ZXing.dll库生成二维码(C#实现)
查看>>