选择排序的基本思想:每一趟从待排序的元素中选出关键字最小(或最大)的元素,顺序放在已排好序的子表里,直到全部元素排序完毕。
2. 堆排序
1. 直接选择排序
/**
* 选择排序
*
* 算法:直接选择排序(Straight Select Sort)
* 输入:待排序元素的数组,待排序元素个数
* 输出:
* 原理:第i趟排序开始时,当前有序区为R[0..i-1],无序区为R[i..n-1],该趟排序从无序区中选出关键字最小的元素R[k],交换R[k]与R[i],形成新的有序区R[0..i]和无序区R[i+1..n-1]
* 过程:
* 1)初始化有序区为空,无序区为R[0..n-1]
* 2)从无序区R[0..n-1]中选出关键字最小的元素并与R[0]交换,形成新的有序区R[0]和无序区R[1..n-1]
* 3) ...
* 4)形成新的有序区R[0..i-1]和无序区R[i..n-1]
* 5)从无序区R[i..n-1]中选出关键字最小的元素并于R[i]交换,形成新的有序区R[0..i]和无序区R[i+1..n-1]
* 6)重复上述操作,直到无序区中没有任何元素
* 7) ...
* 8)最终得到的R[0..n-1]中的元素递增排序
*
* 时间复杂度为O(n^2),空间复杂度为O(1),不稳定的排序方法
*/
void selectSort(RecType R[], int n)
{
int i, j, k;
RecType tmp;
//做第i趟排序
for (i = 0; i < n - 1; i++)
{
k = i;
//在当前无序区R[i..n-1]中选key最小的R[k]
for (j = i + 1; j < n; j++)
if (R[j].key < R[k].key)
k = j; //k记下目前找到的最小关键字所在的位置
//交换R[i]与R[k]
if (k != i)
{
tmp = R[i];
R[i] = R[k];
R[k] = tmp;
}
}
}
2. 堆排序
/**
* 选择排序
*
* 算法:堆排序(Heap Sort)
* 输入:待排序元素的数组(下标从1到n),待排序元素个数
* 输出:
* 定义:1) 小根堆:n个关键字序列K[1..n-1],满足K[i]<=K[2i]且K[i]<=K[2i+1]
2) 大根堆:n个关键字序列K[1..n-1],满足K[i]>=K[2i]且K[i]>=K[2i+1]
* 原理:堆排序是一种树形选择排序方法,在排序过程中,R[1..n]被看做是一棵完全二叉树的顺序存储结构,利用完全二叉树中双亲节点和孩子节点之间的内在关系,在当前无序区中选择关键字最小(或最大)的元素
* 过程:
* 1)针对R[1..n]构建初始堆,此时根节点R[1]为R[1..n]中关键字最大的元素
* 2)交换R[1]与R[n]的位置
* 3) 针对R[1..n-1]继续构建堆,此时根节点R[1]为R[1..n-1]中关键字最大的元素
* 4)交换R[1]与R[n-1]的位置
* 5)针对R[1..n-2]继续构建堆
* 6)...
* 7) 针对R[1..2]继续构建堆,此时根节点R[1]为R[1..2]中关键字最大的元素
* 8) 交换R[1]与R[2]的位置
* 8)最终得到的R[1..n]中的元素递增排序
*
* 时间复杂度为O(nlog2(n)),空间复杂度为O(1),不稳定的排序方法
*/
void heapSort(RecType R[], int n) //
{
int i;
RecType tmp;
for (i = n / 2; i >= 1; i--) //循环建立初始堆
shiftHeap(R, i, n);
for (i = n; i >= 2; i--) //进行n-1趟堆排序,每一趟堆排序的元素个数减1
{
tmp = R[1]; //将最后一个元素与当前区间内R[1]对换
R[1] = R[i];
R[i] = tmp;
shiftHeap(R, 1, i - 1); //筛选R[1]节点,得到i-1个节点的堆
}
}
/**
* 目的:构建R[low..high]的堆
* 作用:大者“上浮”,小者被“筛选”下去
* 原理:假设完全二叉树的某一个节点i,它的左子树和右子树已经是堆,接下来需要将R[2i].key与R[2i+1].key之中的最大者与R[i].key比较。
若R[i].key较小,则将其与最大孩子的关键字交换,这可能会破坏下一级的堆,于是继续采用上述方法构造下一级的堆,直到完全二叉树中节点i构成堆为止。
* 过程:
* 1) 初始化指针i指向R[low],将R[low]的左右孩子中关键字最大的孩子R[j]与R[low]交换,指针i指向交换的孩子R[j]
* 2) 重复上述操作,直到j>high,即堆构建结束
*
*/
void shiftHeap(RecType R[], int low, int high) //调整堆
{
//R[j]是R[i]的左孩子
int i = low, j = 2 * i;
RecType tmp = R[i];
while (j <= high)
{
if (j < high && R[j].key < R[j + 1].key) //若右孩子较大,把j指向右孩子
j++;
if (tmp.key < R[j].key)
{
//将R[j]调整到双亲节点位置上
R[i] = R[j];
//修改i和j值,以便继续向下筛选
i = j;
j = 2 * i;
}
else
break; //筛选结束
}
//被筛选节点的值放入最终位置
R[i] = tmp;
}