排序算法复习之——内部排序算法之——选择排序
选择排序分为:直接选择排序、堆排序
选择排序思想:每次从待序排记录中选出关键字最小的记录,顺序放在已排好的子文件最后,直到全部记录排序完毕。
一、直接选择排序思想:n个记录的直接选择排序可以经过n-1次直接选择排序得到有序结果。
时间复杂度:O(n*n) 空间复杂度:O(1) 不稳定
步骤:
(1) 初始状态,无序区为A[1…n],有序区为空;
(2) 第一次排序:在无序区为A[1…n]中选出最小的记录A[k],将它与无序区的第一个记录A[1]交换,使A[1…1]和A[2…n]分别成为记录个数增加1个的新的有序区和记录个数减少1个的新无序区。
(3) 第i次排序:第i次排序开始时,当前有序区和无序区分别为A[1…i-1]和A[i…n],该次从当前无序区中选出关键字最小的记录A[k],将它与无序区的第1个记录A[i]交换,使A[1…i]和A[i…n]分别为记录个数增加1个的新的有序区和记录个数较少1个的新的无序区。
(4)这样,n个记录的文件的直接选择排序经过(n-1)次直接选择排序得到有序结果。
代码实现:
#include "stdafx.h"
#include <iostream>
using namespace std;
void select_sort(int a[],int len)
{
int i,j,x,l;
for (i=0; i<len; i++)
{
//记录每次遍历时所得的最小元素值及所在位置
x = a[i];
l = i;
for (j=i; j<len; j++)
{
if (a[j]<x)
{
x=a[j];
l=j;
}
}
a[l]=a[i];
a[i]=x;
}
}
int _tmain(int argc, _TCHAR* argv[])
{
int array[9]={54,38,96,23,15,72,64,45,83};
select_sort(array,9);
for (int i=0; i<9; i++)
{
cout<<array[i]<<" ";
}
cout<<endl;
system("pause");
return 0;
}
运行结果:
步骤解析:
排序前数组元素为:54 38 96 23 15 72 60 45 83
第一次排序:此时i=0,遍历整个数组最小元素为15,然后与a[0]交换
结果为:15 38 96 23 54 72 60 45 83
第二次排序:此时i=1,遍历从a[1]开始到数组末尾得到最小元素23,然后与a[1]进行交换
结果为:15 23 96 38 54 72 60 45 83
第三次排序:此时i=2,遍历从a[2]开始到数组末尾得到最小元素38,然后与a[2]进行交换
结果为:15 23 38 96 54 72 60 45 83
……
显然每一次排序都选出了一个最小元素与遍历起始位置的元素进行交换,通过(n-1)次这样的排序,整个数组有序。
二、下面使用一种更加简单易懂的选择排序
使用按引用传递的选择排序
算法思想:(陈述更加明确,我喜欢)
算法的第一次迭代从数组中选择最小的元素,并将它与第一个元素交换;
第二次迭代选择第二小的元素(它是剩余元素中最小的那个),并将它与第二个元素交换;
这个算法持续执行,直到最后一次迭代选择了第二大元素,并将它与倒数第二个位置的元素交换,使最大的元素位于最后一个位置。
经过第i次迭代后,数组中最小的前i个数据项按照升序保存在数组的前i个元素中。
代码实现:
#include <iostream>
using namespace std;
void selectSort(int *const,const int);
void swap(int *const,int *const);
int _tmain(int argc, _TCHAR* argv[])
{
const int arraySize = 10;
int a[arraySize] = {2,4,6,8,10,12,89,68,45,37};
cout<<"Data items in original order\n";
for (int i=0; i<arraySize; i++)
{
cout<<a[i]<<" ";
}
selectSort(a,arraySize);
cout<<"\nData items in ascending order\n";
for (int j=0; j<arraySize; j++)
{
cout<<a[j]<<" ";
}
cout<<endl;
system("pause");
return 0;
}
void selectSort(int *const array,const int size)
{
int smallest; //声明变量smallest,用于保存剩余数组中最小元素的索引
for (int i=0; i<size-1; i++)
{
smallest = i; //将最小元素的索引设置为当前索引
for (int index=i+1;index<size;index++) //对数组中的剩余元素进行循环
{
if (array[index] < array[smallest]) //将每一个元素的值与最小元素的值进行比较
{
smallest = index; //如果当前元素小于最小元素,将当前元素的索引赋予smallest
} //循环结束时,smallest将包含剩余数组中最小的那个元素的索引
}
swap(&array[i],&array[smallest]); //调用swap函数,将这个最小元素放在数组的下一个位置(即交换数组元素array[i]和array[smallest])
}
}
//c++实现函数间的信息隐藏,信息隐藏阻止了swap知道array[i]这个名称,但swap可以使用*element1Ptr作为array[i]的别名
void swap(int *const element1Ptr,int *const element2Ptr)
{
int hold = *element1Ptr;
*element1Ptr = *element2Ptr;
*element2Ptr = hold;
}
运行结果:
三、堆排序:
定义:n个序列,A1,A2,…,An称为堆。
2种不同类型的堆:
小根堆:所有子节点都大于其父节点。即:Ai<A2i且Ai<=A2i+1
大根堆:所有子节点都小于父节点。即:Ai>A2i且Ai>=A2i+1
堆排序是一种树形选择排序,在排序过程中,将R[1…n]看成一颗完全二叉树的顺序存储结构。利用完全二叉树中双亲节点和孩子节点之间的内在关系,在当前无序区中选择关键字最大(或最小)的记录。
时间复杂度:O(n*logn) 空间复杂度:O(1) 不稳定
基本步骤:
(1) 先初始化A[1…n]建成一个大根堆,此堆为初始的无序区。
(2) 将关键字最大的记录A[1]和无序区的最后一个记录A[n]交换,由此得到新的无序区A[1…(n-1)]和有序区A[n],且满足A[1…(n-1)]<=A[n]。
(3) 由于交换后新的根A[1]可能违反堆性质,故应将当前无序区A[1…(n-1)]调整为堆;然后再将A[1…(n-1)]中关键字最大的记录A[1]和该区间的最后一个记录A[n-1]交换,由此得到新的无序区A[1…(n-2)]和有序区A[(n-1)…n],且满足:A[1…(n-2)]<= A[(n-1)…n];同样将A[1…(n-2)]调整为堆。
(4) 对调整的堆重复进行上面的交换,知道无序区只有一个元素为止。
参考资料:《c++程序员手册 》 人民邮电出版社 张京、胡凌云
《c++程序员教程》 电子工业出版社 张良华等翻译
版权所有,引用请标明出处。