这里先介绍简单选择排序,然后在介绍堆排序,(堆排序是选择排序的一种)
选择排序
定义:
选择排序(Selection Sort)是一种简单直观的排序算法。它是每一次从待排序的数据元素中选出最小(或最大)的一个元素,存放在序列的起始位置,直到全部待排序的数据元素排完。选择排序是不稳定的排序方法(比如序列【5,5,3】第一次就将第一个【5】与【3】交换,导致第一个5挪到第二个5后面)。
优化:
在选择排序每次遍历数组中,选择最大数与最小数然后在进行交换。
图解:
代码:
void SelectSort(int* a,size_t n)
{
assert(a);
size_t begin = 0;
size_t end = n-1;
while(begin < end)
{
size_t min = begin, max = end;
for(size_t i=begin; i<=end; i++)
{
//选最大值、最小值的下标
if(a[i] < a[min])
{
min = i;
}
if(a[i] > a[max])
{
max = i;
}
}
//交换
if(max == begin) //(当max==begin时,应进行特殊处理)
{
max = min;
}
swap(a[begin],a[min]);
swap(a[end],a[max]);
++begin;
--end;
}
}
时间复杂度:O(n^2)
堆排序
定义:
堆排序(HeapSort)是指利用堆这种数据结构所设计的一种排序算法,它是选择排序的一种,可以利用数组的快速定位指定索引的元素。
大堆排序的基本思想:
1.先将数据建成一个大堆。
2.再将对顶元素与最后一个元素进行交换,由于交换后新的堆可能违反堆的性质,故应将堆进行调整。
大根堆排序算法的基本操作:
1、建堆 建堆是不断调整堆的过程,从len/2处开始调整,一直到第一个节点。
2.调整堆 利用的思想是比较结点i和它的孩子结点left、right,选出三者最大者,如果最大(小)值不是节点i而是它的一个孩子结点,则交换后会影响这条路径上的其他节点,所以应继续往下调整,直到叶子结点。
3、堆排序:首先是根据元素构建堆,然后将堆的根节点与最后的结点进行交换,将前面len-1个结点继续进行堆调整,在交换根结点。
图解:
升序建大堆小堆问题分析:
时间复杂度:
因为建堆的时间复杂度为O(n),调整堆的时间复杂度是O(lgN),调用了n-1次,所以堆排序的时间复杂度为O(nlgN)
代码:
//堆排序(时间复杂度为O(nlogN))
void AdjustDown(int* a, size_t n, int root) //向下调整算法
{
int parent = root;
int child = parent*2+1;
while(child < n)
{
if(child+1<n && a[child] < a[child+1]) //当存在左孩子时,进行判断
{
++child;
}
if(a[child] > a[parent])
{
swap(a[parent],a[child]);
parent = child;
child = parent*2;
}
else
{
break;
}
}
}
void HeapSort(int *a, size_t n)
{
assert(a);
//建大堆(第一个元素为最大值)
for(int i=(n-1-1)/2; i>=0; --i)
{
AdjustDown(a,n,i);
}
//排序(将第一个元素与最后一个元素进行交换,再进行向下调整算法)
int len = n-1;
while(len > 0)
{
swap(a[0],a[len]);
AdjustDown(a,len--,0);
/* len--;*/
}
}