#include <iostream>
#include <stdio.h>
#include <vector>
using namespace std;
/*
问题:实现一个堆排序
分析:
堆,用数组表示的话:设数组x[1..n],则有根节点为x[1],孩子结点下标为2*i,2*i+1,父节点下标为i/2,
大顶堆:任何结点的值>=其父节点的值
采用小顶堆排序的步骤:
1 步骤1:设置第一个元素为根节点,后续插入的结点放在已有结点后面,采用向上调整方法进行调整;
2 步骤2:输出结果: 拷贝一个堆,然后输出拷贝堆中根节点元素,然后交换根节点元素和最末端元素,将除最末端元素以外部分重新
用向下调整方法调整为堆。
(或者采用队列,先输入根节点,后续弹出结点,输出结点,这样的话需要建立一个结构体对象:包含元素的值和元素的下标,
应该用队列,不能采用队列,因为可能一个孩子结点所在树的元素都小于另一个)
输入:
12(数组元素个数)
19 51 26 40 35 22 17 23 29 15 20 12
输出:
12 15 17 19 20 22 23 26 29 35 40 51
关键:
1采用小顶堆排序的步骤:
1】 步骤1:设置第一个元素为根节点,后续插入的结点放在已有结点后面,采用向上调整方法进行调整;
2】 步骤2:输出结果: 拷贝一个堆,然后输出拷贝堆中根节点元素,然后交换根节点元素和最末端元素,将除最末端元素以外部分重新
用向下调整方法调整为堆。
(或者采用队列,先输入根节点,后续弹出结点,输出结点,这样的话需要建立一个结构体对象:包含元素的值和元素的下标,
应该用队列,不能采用队列,因为可能一个孩子结点所在树的元素都小于另一个)
2 堆排序是选择排序,因为:每次都是选择一个最小的元素,次最小的元素,...(小堆顶的堆顶最小)。
选择排序:第i趟从后面n-i个元素中选取最小的元素作为有序子序列第i个元素
3 不需要临时数组拷贝结果,直接在原始数组上操作
//这边交换n-1趟就可使得数组有序
for(int i = size ; i >= 1 ; i--)//重复size长度次,否则会漏掉一个元素
{
//交换堆顶和最末端元素
swap(results.at(i) , results.at(1));//最后面是最小元素
//然后进行向下调整
adjustDown(results , i - 1);//这里设定长度为当前长度减1,因为最后一个元素不要弄了
}
*/
void swap(int* ptr1 , int* ptr2)
{
int temp = *ptr1;
*ptr1 = *ptr2;
*ptr2 = temp;
}
/*向上调整,先确定数组最后一个元素下标i,然后寻找其父节点下标i/2,判断如果结点的值<父节点的值,就交换结点和父节点,并继续向上调整,
直到满足堆的性质,或者当前结点已经变成根节点
*/
void adjustUp(vector<int>& datas)
{
if(datas.empty())
{
return;
}
int i = datas.size() - 1;//由于第一个元素不做数,因此这里下标要减一
int p;
while(true)
{
//如果当前结点为根节点,直接退出
if(1 == i)
{
break;
}
//寻找父节点下标
p = i / 2;
//如果结点 < 父节点的值,就交换,交换后,需要另当前结点下标变成其父节点下标
if( datas.at(i) < datas.at(p) )
{
swap(datas.at(i) , datas.at(p));
i = p;
}
//如果发现符合堆的性质,直接退出
else
{
break;
}
}
}
//向下调整:从根节点往下,如果发现根节点大于两个孩子结点中的较小值,就交换根节点和两个孩子结点中的较小值,直到处理结点的下标大于数组长度或者符合堆的性质
void adjustDown(vector<int>& datas , int size)
{
if(datas.empty())
{
return;
}
int i = 1;
int c;
while(true)
{
//如果处理结点下标 > 数组长度,直接退出
if(i > size)
{
break;
}
//如果发现:当前结点 > 两个孩子结点中的较大值
c = 2 * i;
//如果孩子结点下标 > 数组长度,无需处理,直接退出
if(c > size)
{
break;
}
//如果该结点还有右孩子结点
if(c + 1 <= size)
{
//如果右孩子 < 左孩子
if( datas.at(c + 1) < datas.at(c))
{
c++;
}
}
//如果结点 > 两个孩子结点中的较大值,交换两个结点,并使得当前结点为其孩子结点
if( datas.at(i) > datas.at(c) )
{
swap(datas.at(i) , datas.at(c) );
i = c;
}
else
{
break;
}
}
}
vector<int> heapSort(vector<int>& datas)
{
vector<int> results;//存放最终的堆
if(datas.empty())
{
return results;
}
int size = datas.size();
results.push_back(-1);
//首先是建堆,采用向上调整,注意向上调整完以后,不需要从根节点继续向下判断是否满足堆,此时一定满足
for(int i = 0 ; i < size ; i++)
{
int value = datas.at(i);
results.push_back(value);
//向上调整
adjustUp(results);
}
//这边交换n-1趟就可使得数组有序
for(int i = size ; i >= 1 ; i--)//重复size长度次,否则会漏掉一个元素
{
//交换堆顶和最末端元素
swap(results.at(i) , results.at(1));//最后面是最小元素
//然后进行向下调整
adjustDown(results , i - 1);//这里设定长度为当前长度减1,因为最后一个元素不要弄了
}
return results;
}
void print(vector<int> results)
{
if(results.empty())
{
cout << "No result " << endl;
}
else
{
int size = results.size();
for(int i = size ; i >= 1 ; i--)//第0个元素不需要输出
{
cout << results.at(i) << " ";
}
cout << endl;
}
}
void process()
{
int num;
vector<int> datas;
int value;
vector<int> results;
while(cin >> num)
{
datas.clear();
for(int i = 0 ; i < num ; i++)
{
cin >> value;
datas.push_back(value);
}
//下面进行堆排序
results = heapSort(datas);
print(results);
}
}
int main(int argc , char* argv[])
{
process();
getchar();
return 0;
}
编程珠玑: 14章 堆 14.1实现一个堆,对数组排序 -------解题总结
最新推荐文章于 2019-08-13 08:58:48 发布