选择类排序总结
所谓选择类排序的思想就是:从数组的中选出最大或最小的,通过多次选择最后达到排序的目的
首先是简单选择排序
思想:每趟扫描中,选出最小的数字放在最前面,然后从第二个数字开始扫描,直到只剩下最后一个数不需要扫描
void EasySelect_Sort(int a[],int n)
{
int k;
for(int i=1;i<n;i++)
{
k=i-1;
for(int j=i;j<n;j++)
{
if(a[j]<a[k])
{
k=j;
}
}
if(k!=(i-1))
{
int temp=a[i-1];
a[i-1]=a[k];
a[k]=temp;
}
}
}
经过分析可以得到,最坏时间复杂度为O(n²),最好时间复杂度为O(n²)
空间复杂度为O(1);
简单选择排序是不稳定的!,例如3,3,2
二.堆排序
上面的简单选择排序中,一趟只能选出一个最大的,一趟比较完之后第二趟重新开始比较,这样一来,第一趟比较时的结果就浪费了,我们如果能够将他们利用起来的话,可想而知,可以提高不少效率,堆排序就是在这样的情况下提出来的
堆排序通过维护堆的数据特点,来实现排序,所谓堆,分为大根堆和小根堆,大根堆就是,根节点大于左子结点且根节点大于右子结点(a[i]>=a[2i]&&a[i]>=a[2i+1])
我们以大根堆为例,分析,堆排序的过程:
堆排序分为:维护堆,建立堆,堆排序三个过程
所谓维护堆:
也就是保持他堆的性质,假设我们现在将原大根堆的根,也就是最大的结点更换为一个比较小的数值,这样,就破坏了 大根堆的性质,维护堆就是此时发挥作用的,维护堆就是要将这样一个已经被破坏的大根堆恢复为大根堆
void Keep_Sort(int a[],int n,int m)
{
int x=a[n];
int k=n;
int i=2*k;
bool finish=false;
while((i<=m)&&(!finish))
{
if(i<m&&a[i]<a[i+1])
{
i+=1;
}
if(a[i]<x)
{
finish=true;
}
else
{
a[k]=a[i];
k=i;
i=2*k;
}
}
a[k]=x;
}
以上代码就是维护堆的代码,注意第一个if中不要忘了第一个条件!!!防止越界,调试半天才调出错误
还有就是建立堆了,
为了建立堆,我们可以从二叉树的第一个非叶子结点开始,倒着不断地维护堆,直到维护到根节点,这样依赖,一个完美的大根堆就建立了
void Create_Heap(int a[],int n)
{
for(int i=n/2;i>=1;i--)
{
Keep_Sort(a,i,n);
}
}
第三步就是堆排序了,
堆排序利用了大根堆的性质,我们不断地取出大根堆的根,然后使出维护堆的神术,直到,堆灯尽油枯,只剩下一个结点,这时候我们的堆排序也完成了,哈哈,有一种,牺牲小堆,完成大排序的错觉
在实际的排序过程中,我们首先建立一个堆,然后将根节点与最后一个元素互换,这样最后一个元素就是最大的了,然后使出维护堆的大招,不过这时候维护堆就只维护前面n-1个数,最后的一个数(也就是最大数)我们把他踢出堆外,直到堆中只剩下一个元素,那他自然是最小的了,这时候所有元素从小到大排列
void Heap_Sort(int a[],int n)
{
Create_Heap(a,n);
for(int i=n;i>=2;i--)
{
int temp=a[i];
a[i]=a[1];
a[1]=temp;
Keep_Sort(a,1,i-1);
}
}
注意,堆排序的最好时间复杂度和最坏时间复杂度都为O(nlog2(n))
空间复杂度为O(1)
堆排序不稳定
可以看出,选择排序基本上都是不稳定的