/*
名称:选择排序—简单选择排序和堆排序
说明:
对于简单选择排序来说,其主要的算法思想是每次从后面没确定的自序列中选取一个最小的(如果是从小到大排序)元素,与当前元素进行交换。其基本的算法时间复杂度为:O(N2)。对于这个算法来说,其比较次数与初始的序列状态没关系,无论如何,都需要进行n(n-2)/2次比较。由于其在确定元素位置的时候用到了交换,这就造成了对于相同关键字的元素,交换之后前后顺序可能发生变化。因此,这是一个不稳定的排序算法。
对于堆排序来说,虽然其逻辑是二叉树的结构,但是其存储的物理结构确确实实是一个顺序数组。(这应该算是一个理解逻辑结构和存储结构区别的好例子了)。其算法思想是:把一列元素看成一个堆,一个父亲结点大于子结点的堆(和二叉排序树不一样),这样你会发现,在这列元素中已经包含了某种特定关系的自序列。而这个关系就是父结点大于子结点,这样在每次排序时,就不用搜索所有元素,只要对特定的元素进行排序即可,即调整序列依然成为堆。在我看来,这应该是堆排序能大大降低时间复杂度的原因。其最终的时间复杂度为O(nlog2n)。还有由于它也是基于交换的,所以它也是一个不稳定的算法。还要补充一点的是,对堆的操作涉及到初始化建堆和对堆进行调整。
*/
#include<iostream>
using namespace std;
//简单选择排序
void SelectSort(int elem[],int n)
{
int _min = 0,temp = 0;
for(int i = 0;i<n-1;++i)
{
_min = i;
for(int j = i+1;j < n;++j)
if(elem[j] < elem[_min]) //选择最小的元素
_min = j;
if(i != _min) //如果不和原来的位置相等,则进行交换
{
temp = elem[i];
elem[i] = elem[_min];
elem[_min] = temp;
}
}
}
//向下调整
void AdjustDown(int elem[],int pos,int len)
{
elem[0] = elem[pos]; //elem[0] 暂存
for(int i = pos*2;i <= len; i*=2) //向下筛选
{
if(i < len && elem[i]<elem[i+1]) //取key值较大的子结点的下标
++i;
if(elem[0] >= elem[i]) //筛选结束
break;
else
{
elem[pos] = elem[i]; //将elem[i]调整到双亲节点
pos = i; //修改k值,以便继续向下筛选
}
}
elem[pos] = elem[0]; //被筛选结点的值放入最终的位置
}
//初始化建立大根堆
void BuildMaxHeap(int elem[],int len)
{
for(int i = len/2;i>0;--i)
AdjustDown(elem,i,len);
}
//堆排序
void HeapSort(int elem[],int len)
{
int t = 0;
BuildMaxHeap(elem,len); //初始化建立大根堆
for(int i = len;i>1;--i)
{
cout<<elem[1]<<" ";
t = elem[1];
elem[1] = elem[i];
elem[i] = t;
AdjustDown(elem,1,i-1); //向下调整,保持堆的结构
}
}
总结:写了几个经典的排序算法后,隐隐的有点感觉,有很多改进的排序算法都是利用已知序列间的部分有序自序列,或者自序列中含有某种特定关系的。当然这种自序列可能一开始并不存在,需要自己去创造。比如说堆排序、shell排序。其实总某种角度上说,快速排序也算是,因为其每次选取基准后,就把原序列分成了两个自序列,使得下一次排序时只要在自序列中进行就可以了。