#include <iostream>
#include "iomanip"
using namespace std;
/*
选择排序思想:
每一趟(如第i趟)从后面n-i+1个待排序的元素中选出关键字最小的元素,
作为有序子序列(前i-1个有序)的第i个元素,直至n-1趟结束。
因为最后一个元素是不用再排序的(没有比它再小的元素了,他是最后一个)
*/
void selectSort(int a[] , int n)
{
int min;//记录最小元素的下标
for(int i = 1; i <= n ; i++)
{
min = i;
for(int j = i+1 ; j <= n ; j++)
{
if(a[j] < a[min])
{
min = j;
}
}
swap(a[min],a[i]);
}
}
/*
选择排序改进的算法思想:
在选择排序每趟选取最大元素的过程中,判断接下来的元素是否有序。
倘若有序则停止。否则,继续。
该算法和先前的选择排序算法差别在于:时间复杂度与初始序列有关。
*/
int _max(int a[] , int n , int &m)
{
int pos = 0;//确定从a[0:n-1]最大元素的下标
for(int i = 1 ; i < n ; i++)
{
if(a[i] > a[pos])
{
pos = i;//记录最大元素的位置
m++;
//记录递增的正序对的个数。如 1 2 3 4那么该判断语句就会记录m=4
//那么如果m等于p-1,即p之前的元素都有序则结束。
}
}
return pos;
}
void selectSortModify(int a[] , int n)
{
int m = 0;//正序对个数,如果a[1]-a[i]是正序那么m会在max函数中记录a1到ai的个数。
for(int p = n ; p > 1 ; p--)
{
int j = _max(a , p , m);
swap(a[p-1] , a[j]);//因为p是从n开始,而数组下标是1到(n-1)
if(m == p-1) break;
}
}
/*
堆排序思想:
1、建堆 2、进行堆排序输出。而这两个部分都要借助堆的调整(因为每次输出元素会影响树)
*/
void headAdjust(int a[] , int k , int n)
{
a[0] = a[k];
for(int i = 2*k ; i <= n ; i*= 2)
{
if(i < n && a[i] < a[i+1])
{//i<n保证k有左右孩子节点;
//a[i] < a[i+1]保证i所指为子节点最大值。
i++;
}
if(a[0] >= a[i]) break;//满足大根堆的要求
else
{
a[k] = a[i];//把i(k的最大子节点)换到k的位置,保证k大于子节点值。
k = i;
//修改调整的元素,因为此时是a[k](最初的k)换在了i的位置上,
//而且该位置还不一定是最终位置,
//还要继续判断其和孩子节点是否满足大根堆的要求。
}
}
a[k] = a[0];//找到最初k的最终位置。
}
void BuildHeap(int a[] , int n)//对下标1-(n-1)个元素进行构建堆
{//根据完全二叉树性质n/2为分支节点
for(int i = n/2 ; i > 0 ; i--)
{//从n/2到1,反复调整堆。因为在所选的元素进行调整后,可能会对其继续调整。
//比如i=n/2对其孩子调整完后,n/2的父节点比它还小
//(因为此时表是无序的,所以可能发生)
//那么其父节点就要往下调整,来保证大根堆,n/2的节点可能往上调。
headAdjust(a , i , n);
}
}
void HeapSort(int a[], int n)
{
BuildHeap(a,n);
for(int i = n ; i > 1 ; i--)
{
swap(a[i] , a[1]);//第一个元素和最后一个元素互换
headAdjust(a , 1 , i-1);
}
}
int main()
{
int a[10] = {1,9,2,0,12,4,5,3,7,10};
selectSortModify(a , 10);
for(int i = 0 ; i < 10 ; i++)
printf("%d ",a[i]);
}