您的位置:首页 > 其它

堆排序

2014-04-25 11:57 211 查看
二叉堆的性质

二叉堆是一颗完全二叉树,最后一层的叶子从左到右排列,其它的每一层都是满的

最小堆父结点小于等于其每一个子结点的键值,最大堆则相反

每个结点的左子树或者右子树都是一个二叉堆

堆的存储

通常堆是通过一维数组来实现的。在起始数组为 0 的情形中:

父节点i的左子节点在位置 (2*i+1);

父节点i的右子节点在位置 (2*i+2);

子节点i的父节点在位置 floor((i-1)/2);

思路:
首先我们需要把树通过调整变为最小堆,则最小堆的根节点(也就是数组的第一个元素)即满足了是该序列的最小值,然后我们把它和数组的最后一个元素交换(目的就是把当前的最小值保存到数组的尾部),接下来我们对新形成的树(除去最后一个节点,因为最后一个节点已经是当前最小值了)进行调整,再次变为新的最小堆,最小堆的根节点即为当前新的最小值,再把该最小值与数组的倒数第二个数交换,则数组的倒数第二个数子即为序列的第二小的数字;现在,以此类推,直到新的树中只有一个节点为止,则此时,保存在数组中的数字满足从大到小的顺序。这就是堆排序作为选择排序的一种的排序过程。

把根节点调节到合适的节点:

void MinHeapFixdown(int *a, int i, int n)  //i表示当前节点,n表示节点数
{
int j,temp;  //j为子节点
temp = a[i]; //父节点放到temp
j = i*2 + 1;

while (j < n)
{
if(j + 1 < n && a[j+1] < a[j])  //如果i的子节点有两个,选择最小那个,即为j
j++;
if(temp <= a[j])               //如果j比父节点要大,即退出循环
break;
a[i] = a[j];                  //把较小的节点与父节点交换,即上移
i = j;
j = 2*i+1;
}

a[i] = temp;
}


建立一个小顶堆,从第一个非叶子节点开始:

void MakeMinHeap(int *a, int n)
{
for (int i = n/2 - 1; i >=0; i--)
{
MinHeapFixdown(a, i, n);
}
}


这时进行堆排序即可:

void MinHeapSort(int *a, int n)
{
MakeMinHeap(a, n);
for (int i = n - 1; i > 0; i--)
{
swap(a[i], a[0]);
MinHeapFixdown(a, 0, i);
}
}


实例:



先建立小顶堆,得到如图所示:



在得到是有序最大堆

代码如下:

// chahch.cpp : 定义控制台应用程序的入口点。
//

#include "stdafx.h"
#include <iostream>

using namespace std;

void MinHeapFixdown(int *a, int i, int n) //i表示当前节点,n表示节点数 { int j,temp; //j为子节点 temp = a[i]; //父节点放到temp j = i*2 + 1; while (j < n) { if(j + 1 < n && a[j+1] < a[j]) //如果i的子节点有两个,选择最小那个,即为j j++; if(temp <= a[j]) //如果j比父节点要大,即退出循环 break; a[i] = a[j]; //把较小的节点与父节点交换,即上移 i = j; j = 2*i+1; } a[i] = temp; }

void MakeMinHeap(int *a, int n) { for (int i = n/2 - 1; i >=0; i--) { MinHeapFixdown(a, i, n); } }
void MinHeapSort(int *a, int n) { MakeMinHeap(a, n); for (int i = n - 1; i > 0; i--) { swap(a[i], a[0]); MinHeapFixdown(a, 0, i); } }
int _tmain(int argc, _TCHAR* argv[])
{
int arr[] = {53,36,30,91,47,12,24,85};
MinHeapSort(arr, 8);

for (int i = 0; i < 8; i++)
{
cout << arr[i] << " ";
}

return 0;
}


-------------------------------------------------------

如果建立有序最小堆呢

即将上述上述建立的小顶堆修改为大顶堆即可:

//建立大顶堆
void MaxHeapFixdown(int *a, int i, int n) //i表示当前节点,n表示节点数
{
int j,temp; //j为子节点
temp = a[i]; //父节点放到temp
j = i*2 + 1;

while (j < n)
{
if(j + 1 < n && a[j+1] > a[j]) //如果i的子节点有两个,选择最大那个,即为j
j++;
if(temp >= a[j]) //如果j比父节点要小,即退出循环
break;
a[i] = a[j]; //把较大的节点与父节点交换,即上移
i = j;
j = 2*i+1;
}

a[i] = temp;
}

void MakeMinHeap(int *a, int n)
{
for (int i = n/2 - 1; i >=0; i--)
{
MaxHeapFixdown(a, i, n);
}
}

如图所示:



在此基础上再排序。得到最终有序最小堆



代码如下:

// chahch.cpp : 定义控制台应用程序的入口点。
//

#include "stdafx.h"
#include <iostream>

using namespace std;

//建立小顶堆
void MinHeapFixdown(int *a, int i, int n) //i表示当前节点,n表示节点数 { int j,temp; //j为子节点 temp = a[i]; //父节点放到temp j = i*2 + 1; while (j < n) { if(j + 1 < n && a[j+1] < a[j]) //如果i的子节点有两个,选择最小那个,即为j j++; if(temp <= a[j]) //如果j比父节点要大,即退出循环 break; a[i] = a[j]; //把较小的节点与父节点交换,即上移 i = j; j = 2*i+1; } a[i] = temp; }

//建立大顶堆
void MaxHeapFixdown(int *a, int i, int n) //i表示当前节点,n表示节点数
{
int j,temp; //j为子节点
temp = a[i]; //父节点放到temp
j = i*2 + 1;

while (j < n)
{
if(j + 1 < n && a[j+1] > a[j]) //如果i的子节点有两个,选择最大那个,即为j
j++;
if(temp >= a[j]) //如果j比父节点要小,即退出循环
break;
a[i] = a[j]; //把较大的节点与父节点交换,即上移
i = j;
j = 2*i+1;
}

a[i] = temp;
}

void MakeMaxHeap(int *a, int n)
{
for (int i = n/2 - 1; i >=0; i--)
{
MaxHeapFixdown(a, i, n);
}
}

void MaxHeapSort(int *a, int n)
{
MakeMaxHeap(a, n);
for (int i = n - 1; i > 0; i--)
{
swap(a[i], a[0]);
MaxHeapFixdown(a, 0, i);
}
}

int _tmain(int argc, _TCHAR* argv[])
{
int arr[] = {53,36,30,91,47,12,24,85};
MaxHeapSort(arr, 8);

for (int i = 0; i < 8; i++)
{
cout << arr[i] << " ";
}

return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: