选择排序思想就是第一次选出关键字最小的的记录作为第一个,然后就是除了第一个元素外再去选取一个最小的元素放在第二个,依次循环知道全部记录有序为止。但是这样效率会有点低,为了改进算法的效率而又提出了树形选择排序和堆排序,下面进行一一介绍:
(1)简单选择排序:选择排序的基本思想:每一趟在n-i+1(i=1,2,3,…,n-1)个记录中选取关键字最小的记录作为有序序列中第i个记录,此排序算法比较简单,实现起来也没有那么复杂,就不在详细赘述了,只看一个例子就可以了。例如:
代码如下:
void SelectSort(int a[], int length)
{
for (size_t i = 1; i < length; i++)
{
size_t k = i;
for (size_t j = k+1; j < length; j++)
{
if (a[k] > a[j])
{
k = j;
}
}
if (k != i)
{
swap(a[k], a[i]);
}
}
}
性能分析: 时间复杂度 : T(n) = O(n^2) ,空间复杂度 S(n) = O(1)
(2)树形排序
基本思想:
a. 树形排序必须首先构造个满的的二叉树,不够的时候全部补空,对于一个含有n个元素的数组,首先两两结合,作为此完全二叉树的最后一个层
b. 选取最小值做为两个节点的父节点,其父节点作为倒数第二层
c. 把倒数第二层两两结合,选取最小值作为该满二叉树的倒数第三层
d. 依次类推直该二叉树的根节点,此根节点就是最小的值。将在数组中的此数置空,对数组重新进行上述操作就可以得到次大值。最终得出排序结果
例如:数组 10 8 12 21 14 15 53
由于只有七个元素不能构成满的二叉树,所以需要添加一个空的节点构成满的二叉树,并得出最小值8
8的位置置空,重新构造二叉树,获得次小值10,依此类推
代码如下:
//初始化数组,a[]为原数组,b[]为存储二叉树的数组
void InitArr(int b[],int a[], int k,int length)
{
for (size_t i = 1; i <= pow(2,k-1)-1; i++)
{
b[i] = 11111;
}
for (size_t i = 0; i < length; i++)
{
b[ i + (size_t)pow(2,k-1)] = a[i];
}
}
//k为树的深度,len为原数组长度
void TreeSort(int b[],int a[], int k,int len)
{
int length = pow(2, k) - 1;
for (size_t j = 1; j < k; j++)
{
for (size_t i = 2; i <= length; i++)
{
if (b[i] < b[i / 2])
{
b[i / 2] = b[i];
}
}
}
cout << b[1]<<" ";//输出最大值
for (size_t i = 0; i < len; i++)
{
if (a[i] == b[1])
{
a[i] = 11111;//置空
break;
}
}
InitArr(b, a, k, len);//每次选取一个人后要初始化数组重新
}
(3)堆排序,分为两步第一步为初建堆,第二步为调整堆,由于初建堆需要用到调整堆,所以先来学习如何调堆
调整堆,算法思想:
a.首先将于堆相应的完全二叉树根结点中的记录移出,该记录称为待调整记录。此时根结点相当于空结点,从空结点的左右孩子中选出一个关键字较大的记录,如果该记录的关键字大于待调整记录的关键字,则将该记录上移至空结点中。
b.此时,原来那个关键字较大的子结点相当于空结点,从空结点的左右孩子中选出一个关键字较大的记录,如果该记录的关键字仍大于待调整记录的关键字,则将该记录上移至空结点中。
c.重复上述移动过程,直到空结点左右孩子的关键字均小于待调整记录的关键字。此时,将待调整记录放入空结点即可。
例如:原树为下面的一棵树
则调整为
代码实现:
void sift(int a[], int k, int m)//调整下标为k的结点
{
int t = a[k];//“根”结点的左孩子
int i = k;
int j = 2 * k;
bool finesh = false;
while (j<=m && !finesh)
{
if (j<m && a[j]< a[j+1] ) /*在左右子树中选出最大的*/
{
j++;
}
if (t > a[j])
{
finesh = true;/*筛选结束*/
}
else /*继续筛选*/
{
a[i] = a[j];
i = j;
j = i * 2;
}
}
a[i] = t;
}
初建堆,算法思想:从标号最大的有孩子的点i开始筛选,(i=n整除2),然后i减 1,直到i为1为止。
代码实现:
void CreateHeap(int r[], int length)
{
int n = length;
for (size_t i = n / 2; i >= 1; i--)
{
sift(r, i, n);
}
}
结合上两步 堆排序算法实现算法思想:
a. 将待排序记录按照堆的定义建初堆;
b. 将堆顶元素与堆中最后一位元素交换。调整剩余的记录序列,将前n-i ,个元素重新建成为一个新堆;
c. 重复步骤b,进行n-1次。
代码实现:
void CreateHeap(int r[], int length)
{
int n = length;
for (size_t i = n / 2; i >= 1; i--)
{
sift(r, i, n);
}
}
void HeapSort(int r[], int n)
{
CreateHeap(r, n);//初建堆
for (size_t i = n; i >= 2; i--)
{
swap(r[1], r[i]);
sift(r, 1, i - 1);
}
}
性能分析: 时间复杂度 : T(n) = O(nlog2(n))
小结::
排序算法 | 时间复杂度 | 最好情况 | 最坏情况 | 空间复杂度 | 稳定性 |
---|---|---|---|---|---|
简单选择排序 | O(n^2) | O(n^2) | O(n^2) | O(1) | 稳定 |
树形选择排序 | O(nlog2(n)) | O(nlog2(n)) | O(log2(n) | O(n) | 不稳定 |
堆排序 | O(nlog2(n)) | O(nlog2(n)) | O(nlog2(n)) | O(1) | 不稳定 |