选择排序—简单选择排序(Simple Selection Sort)
1. 基本思想
在要排序的一组数中,选出最小(或者最大)的一个数与第1个位置的数交换;然后在剩下的数当中再找最小(或者最大)的与第2个位置的数交换,依次类推,直到第n-1个元素(倒数第二个数)和第n个元素(最后一个数)比较为止。
2. 排序流程
第一趟,从n 个记录中找出关键码最小的记录与第一个记录交换;
第二趟,从第二个记录开始的n-1 个记录中再选出关键码最小的记录与第二个记录交换;
以此类推…..
第i 趟,则从第i 个记录开始的n-i+1 个记录中选出关键码最小的记录与第i 个记录交换,
直到整个序列按关键码有序。
3. 算法实现
简单选择排序
每次只选择一个最值。
代码
#include <stdio.h>
#define N 9
int source[3][N]={{4,1,5,7,9,3,2,8,6},{1,2,3,4,5,6,7,8,9},{9,8,7,6,5,4,3,2,1}};
int *arr;
int step=0;
int nFor=0; //循环次数
int nCompare=0; //比较次数
int nSwap=0; //交换次数
#define SWAP(x,y) do{x^=y;y^=x;x^=y;nSwap++;}while(0)
#define ERR_NULL 1
#define ERR_NOTINT 2
int str2int(char*,int*);
void print()
{
int i;
printf("No.%d\t",step);
for(i=0;i<N;i++)
printf("%d ",arr[i]);
printf("\tnFor:%2d nCompare:%2d nSwap:%2d\n",nFor,nCompare,nSwap);
}
/* 找到第n到N-1中的最小值,n数组中的起始项 */
int findMin(int n)
{
int i,min,tmp;
tmp=arr[n];
min=n;
for(i=n+1;i<N;i++)
{
if(tmp>arr[i]) /* 分别比较大小,找出最小值 */
{
tmp=arr[i];
min=i;
}
nCompare++;
}
return min;
}
void sort()
{
int min,i,j;
for(i=0;i<N;i++) /* 每趟选择一个最小值 */
{
step++;
nFor++;
min=findMin(i);
if(min!=i) /* 如果第一个不是最小值,则交换最小值到位置i上 */
SWAP(arr[i],arr[min]);
nCompare++;
print();
}
}
void main(int argc, char **argv)
{
int kind=0,err=0;
if(argc>1)
{
err=str2int(argv[1],&kind);
if(err==0)
{
printf("[%s] args:%d\n",argv[0],kind);
}
else if(err==ERR_NULL)
{
printf("Args can not be NULL!\n");
}
else
{
printf("Args should be a int!\n");
}
}
if(kind<3)
{
arr=source[kind];
printf("Before...\n");
print();
printf("Sorting...\n");
sort();
printf("Sorted.\n");
}
else
{
printf("Out of Range!");
}
}
/* 字符串转整形 */
int str2int(char *in,int *out)
{
int i=0,n=0;
if(in[0]=='\0') return ERR_NULL; //字符串为空
//从字符串中转出整形
do
{
if(in[i]>='0' && in[i]<='9') //判断每个字符是否为数字
{
n=n*10 + in[i] - '0';
i++;
}
else //不是整形数
{
return ERR_NOTINT;
}
}while(in[i]!=0);
*out=n;
return 0;
}
结果
Before...
No.0 4 1 5 7 9 3 2 8 6 nFor: 0 nCompare: 0 nSwap: 0
Sorting...
No.1 1 4 5 7 9 3 2 8 6 nFor: 1 nCompare: 9 nSwap: 1
No.2 1 2 5 7 9 3 4 8 6 nFor: 2 nCompare:17 nSwap: 2
No.3 1 2 3 7 9 5 4 8 6 nFor: 3 nCompare:24 nSwap: 3
No.4 1 2 3 4 9 5 7 8 6 nFor: 4 nCompare:30 nSwap: 4
No.5 1 2 3 4 5 9 7 8 6 nFor: 5 nCompare:35 nSwap: 5
No.6 1 2 3 4 5 6 7 8 9 nFor: 6 nCompare:39 nSwap: 6
No.7 1 2 3 4 5 6 7 8 9 nFor: 7 nCompare:42 nSwap: 6
No.8 1 2 3 4 5 6 7 8 9 nFor: 8 nCompare:44 nSwap: 6
No.9 1 2 3 4 5 6 7 8 9 nFor: 9 nCompare:45 nSwap: 6
Sorted.
改进—二元选择排序
简单选择排序,每趟循环只能确定一个元素排序后的定位。我们可以考虑改进为每趟循环确定两个元素(当前趟最大和最小记录)的位置,从而减少排序所需的循环次数。改进后对n个数据进行排序,最多只需进行[n/2]趟循环即可。
代码
void sort()
{
int max,min,i,j;
for(i=0;i<=N/2;i++) /* 每趟选择一个最小值 */
{
step++;
nFor++;
min=i;
max=N-1-i;
for(j=i;j<=N-1-i;j++) /* 循环找出最大值最小值放到数组的两端 */
{
nCompare++;
if(arr[j]<arr[min]) /* 记录最小值 */
{
min=j; continue; /* 如果已经记录了最小值,肯定不会是最大值 */
}
nCompare++;
if(arr[j]>arr[max]) /* 记录最大值 */
{
max=j;
}
}
/* 如果位置i不是最小值,则交换最小值到位置i上;存在i位置上的值是最大值的情况,不能直接交换。 */
if(min!=i)
{
SWAP(arr[i],arr[min]);
if(max==i) max=min; /* 如果最大值在位置i,交换后最大值在位置min;同时如果最小值在N-1-i,则只需交换一次。 */
}
if(max!=(N-1-i))
{
SWAP(arr[N-1-i],arr[max]);
}
print();
}
}
结果
Before...
No.0 4 9 6 7 1 3 2 8 5 nFor: 0 nCompare: 0 nSwap: 0
Sorting...
No.1 1 5 6 7 4 3 2 8 9 nFor: 1 nCompare:17 nSwap: 2
No.2 1 2 6 7 4 3 5 8 9 nFor: 2 nCompare:28 nSwap: 3
No.3 1 2 3 5 4 6 7 8 9 nFor: 3 nCompare:36 nSwap: 5
No.4 1 2 3 4 5 6 7 8 9 nFor: 4 nCompare:41 nSwap: 6
Sorted.
4. 算法分析
在简单选择排序过程中,所需移动记录的次数比较少。最好情况下,即待排序记录初始状态就已经是正序排列了,则不需要移动记录。最坏情况下,即待排序记录初始状态是按第一条记录最大,之后的记录从小到大顺序排列,则需要移动记录的次数最多为3(n-1)。
简单选择排序过程中需要进行的比较次数与初始状态下待排序的记录序列的排列情况无关。当i=1时,需进行n-1次比较;当i=2时,需进行n-2次比较;依次类推,共需要进行的比较次数是(n-1)+(n-2)+…+2+1=n(n-1)/2,即进行比较操作的时间复杂度为O(n^2),进行移动操作的时间复杂度为O(n)。
简单选择排序是不稳定排序。