基本排序之堆排序
堆排序:
它可以看做是完全二叉树的结构。
分为大根堆和小根堆。如果父节点大于左右节点就是大根堆,父节点小于左右子节点就是小根堆。本文写大根堆的排序。
设当前节点为i的话:
子节点:2*i+1,2*i+2
父节点:(i-1)/2
概述:
(1)建立大根堆(HeapInsert):把当前节点和父节点比较,如果大于父节点,那么就上升。如果小于父节点,那么不做变动。
(2)调整堆(Heapify):建立大根堆后,现在的堆每一个父节点都大于其左右两个子节点。那么最顶部就是最大的值。此时应当:
i) 把最顶部的元素和最后一个元素交换,让最大的元素处于末尾。
ii) 调整,如果现在最顶部元素的值不是最大值,也就是说,把它和它的左右子节点中大的那个节点比较,如果小于这个子节点,那么这个子节点和它交换,父节点下沉。
iii) 不断比较,直至不能下沉为止。然后重复前两步操作,直至整个数组排好。
其中排好的标志:我们先定义个一个长度表示建立大根堆之后的数组长度,每一次(2)(i)之后,表示一个数排好了,那么这个数组长度就减去1,如果这个数组长度为0,那么排序也就结束了。
时间复杂度:
O(NlogN)
算法稳定性:
不稳定算法。
代码如下:
#include <iostream>
#include <vector>
#include <random>
using namespace std;
class HeapSort_
{
public:
void HeapSort(vector<int> &v);
void HeapInsert(vector<int> &v,int index);
void Heapify(vector<int> &v, int size);
void swap(vector<int> &v, int a, int b);
};
void HeapSort_::HeapSort(vector<int> &v)
{
if (v.size() < 2)
{
return;
}
for (int i = 0; i < v.size(); i++)
{
HeapInsert(v,i);
}
for (int size = v.size()-1; size > 0; size--)
{
swap(v, 0, size);
Heapify(v, size);
}
}
//建立大根堆
void HeapSort_::HeapInsert(vector<int> &v,int index)
{
//如果大于父节点就交换,使得大的值一直处于上部直至顶部
while (v[index] > v[(index - 1) / 2])
{
swap(v, index, (index - 1) / 2);
index = (index - 1) / 2;
}
}
void HeapSort_::Heapify(vector<int> &v,int size)
{
int index = 0;
int left = index * 2 + 1;
//如果没有左节点就不判断,所以left<size
while (left < size)
{
//如果存在右节点,那么返回比较大的数的下标
int max = left + 1 < size && v[left + 1] > v[left] ? left + 1 : left;
max = v[max] > v[index] ? max : index;
//如果子节点小于父节点就返回index,说明大的值还是父节点
if (max == index)
break;
//如果子节点大于父节点,那么交换,并且把子节点的位置赋值给当前位置index作为父节点位置
//左节点重新赋值
swap(v, max, index);
index = max;
left = 2 * index + 1;
}
}
void HeapSort_::swap(vector<int> &v, int a, int b)
{
int temp = v[a];
v[a] = v[b];
v[b] = temp;
}
int main()
{
HeapSort_ h;
static default_random_engine e;
static uniform_int_distribution<int> u(-34, 87);
vector<int> v(15);
for (int i = 0; i < 15; i++)
{
v[i] = u(e);
}
h.HeapSort(v);
for (int i = 0; i < 15; i++)
{
cout << v[i] << " ";
}
return 0;
}