1,简单选择排序
- 基本思想:每一趟在n-i+1个记录中选取关键字最小的的记录作为有序序列中的第i个记录。一趟排序的操作为:通过N-i次关键字间的比较,从N-i+1个记录中,找出关键字最小的记录,并将它与第i个记录交换。
- 主要操作是比较和移动,无论初始序列如何,比较次数不变,都是n(n-1)/2;当初始序列为正序时,移动次数最少为0,当为逆序时,移动次数为最大,为3(n-1)。
- 时间复杂度:最好,最坏都是O(n^2)。
- 空间复杂度:O(1)。
- 不是稳定的排序算法。比如:排序前 2 4 4^ 3。排序后 2 3 4^ 4。
代码如下:
void selectSort(SqList &L){
int i,j;
int min;
int temp;
int minV;
for (i=0;i<L.length;i++)
{
min=i;
minV=L.list[i];
for(j=i;j<L.length;j++)
if(L.list[j]<minV)
{
min=j;
minV=L.list[j];
}
if(i!=min)
{
temp=L.list[i];
L.list[i]=L.list[min];
L.list[min]=temp;
}
}
}
void main()
{
SqList St;
printf("***********************************************\n");
printf(" 简单选择排序算法 \n");
printf("***********************************************\n");
printf("请输入需要排序的关键字个数N (N<%d):",MAXSIZE);
scanf("%d",&St.length);
printf("\n");
printf("请输入%d个待排序的关键字:\n",St.length);
for (int i=0;i<St.length;i++)
scanf("%d",&St.list[i]);
selectSort(St);
printf("\n");
printf("输出排序结果:");
for(int j=0;j<St.length;j++)
printf("%d ",St.list[j]);
printf("\n");
printf("\n");
}
运行结果如下:
2,堆排序
- 基本思想:堆对应的一维数组可以看做是一个完全二叉树,所有非终端的结点的值均不大于(或不小于)其左右孩子结点的值。那么,输出根结点的值一定是最小的元素,再将剩下的n-1个元素调整为堆,再输出根结点,为第二小的元素,如此反复,最终输出的序列就是有序的序列。
- 堆排序有两个问题需要解决:(1),如何将一个无序序列建成一个堆(建初始堆)。(2),如何在输出堆顶后,调整剩下的元素,使之重新成为一个新堆(筛选)。
- 筛选:输出堆顶元素后,将堆中最后一个元素代替输出的堆顶元素,但是此时,已破坏了堆的特性了, 将该元素与左右子树的根结点比较,选出最小的子树根结点并与之交换,若交换后,破坏了子树的堆特性 ,继续向下调整,选出最小子树的根结点并与之交换,直到到叶子结点为止。筛选是自上而下的调整
- 建初始堆:将一个具有n个元素的无序数组序列看成是一个完全二叉树,从 最后一个非终端结点(不大于n/2的最大整数这个结点)开始向上调整,使每棵子树满足堆的特性,直到根结点。建初始堆是自下而上的调整。
- 时间复杂度:最好最坏都是O(nlogn),想比快速排序来说,这是堆排序最大的优点。
- 空间复杂度:O(1)。
- 不是稳定的排序算法。
- 适合N很大的排序序列
- 对初始序列不敏感。
代码如下:
#include "stdafx.h"
#include<stdio.h>
#define MAXSIZE 40
struct SqList{
int list[MAXSIZE+1];
int length;
};
/*
此函数的功能是:对于序列[s……m],除L.list[s]这个元素外,剩余元素都满足堆的定义
对这个元素进行进行调整,使之满足堆的定义。另外,此堆是个大顶堆,
即所有非终端结点的值均大于或等于其左右孩子的结点的值。
建大顶堆而不是小顶堆的原因:我们排序最终的结果是要使序列呈非递减排列,也就是最终要使
第一个元素为最小的,最后一个元素为最大的,那么建大顶堆,最大元素再第一个,通过与最后
一个元素交换,就变成了最后一个,也就是说每次使最大的元素成为堆顶,再与没有被交换过的
最后一个元素交,当交换到只剩一个元素时,序列也就呈非递减状态了。
变量s:待调整的元素序号。
变量m;序列的长度
*/
void HeapAdjust(SqList&L,int s,int m)
{
int i;
int rc=L.list[s];//暂时保存待调整元素
for(i=2*s;i<=m;i*=2)//为什么有等于号?因为需要向下调整到叶子结点,i=m,代表s这个结点只有左叶子结点,小于代表有左右叶子结点
{
if(i<m&&L.list[i]<L.list[i+1])//为什么是i<m而不是i<=m呢?当i=m时,只有左叶子结点,所以没法比较左右结点值的大小,
++i; //也就是说必须保证s这个结点的左右孩子结点同时存在。
if(rc>=L.list[i]) //如果待调整的值大于或等于找到的结点的值,则无需向下调整了。
break;
L.list[s]=L.list[i]; //将孩子结点的值向上移动到s结点(父结点)
s=i; //同时继续向下调整。
}
L.list[s]=rc; //将待调整的元素rc放在找到的最终位置上
}
void HeapSort(SqList &L){
int i,j;
for(i=L.length/2;i>0;i--) //对于无序序列的待排元素,从中间向前每个元素开始调整,建立大顶堆。
HeapAdjust(L,i,L.length);
for(j=L.length;j>1;j--) //将堆顶元素和最后一个元素交换,同时将指向最后一个元素的位置前移,再进行调整。
{
L.list[0]=L.list[j];
L.list[j]=L.list[1];
L.list[1]=L.list[0];
HeapAdjust(L,1,j-1);
}
}
void main()
{
SqList St;
printf("***********************************************\n");
printf(" 堆排序算法 \n");
printf("***********************************************\n");
printf("请输入需要排序的关键字个数N (N<%d):",MAXSIZE);
scanf("%d",&St.length);
printf("\n");
printf("请第一个数输入0,为暂存单元,再输入%d个待排序的关键字值:\n",St.length);
for (int i=0;i<=St.length;i++)
scanf("%d",&St.list[i]);
HeapSort(St);
printf("\n");
printf("输出排序结果:");
for(int j=1;j<=St.length;j++)
printf("%d ",St.list[j]);
printf("\n");
printf("\n");
}
运行结果如下: