本文原创,转载请注明来自http://blog.csdn.net/j903829182/article/details/38018507
/*
1.排序分为内排序和外排序2种。内排序是把待排数据元素全部调入内存中进行的排序。如果数据元素的数量太大,
需要分批导入内存中。分批导入内存的数据元素排好序后再分批导出到磁盘或磁带等外存介质中的排序方法称作为
外部排序。
2.排序有:直接插入排序,希尔排序,直接选择排序,堆排序,冒泡排序,快速排序,二路归并排序和基数排序
3.排序的好坏标准:时间复杂度,空间复杂度,稳定性(当使用主关键字排序时,任何排序算法的排序结果必定是相同的。
但当使用次关键字排序时,其排序结果有可能相同,也有可能不同。设待排序的数据元素共有n个,设ki和kj分别表示第
i个数据元素的关键字和第j个数据元素的关键字,设Ri和Rj分别表示第i个数据元素和第j个数据元素。若Ki=Kj,且在排序
之前数据元素Ri排在数据元素Rj之前,在排序之后数据元素Ri仍排在Rj前边的排序算法称作稳定的排序算法;否则,称作不稳定的
拍戏算法。稳定的排序算法是通常是应用问题所希望的,因此,排序算法的稳定性是衡量排序算法好坏的一个重要标准。)
*/
#include<stdio.h>
#include<malloc.h>
typedef int KeyType;
typedef struct{
KeyType key;
}DataType;
/*
1.插入排序的基本思想:从初始有序的子集合开始,不断地把新的数据元素插入到已排列有序子集合的合适位置,使子集合中数据元素的
个数不断增多,当子集合等于集合时,插入排序算法结束。常用的插入排序有直接插入排序和希尔排序2种。
2.直接插入排序的基本思想:顺序的把待排序的数据元素按其关键字值的大小插入到已排序数据元素子集合的合适位置。子集合的数据元素个数
从只有一个数据元素开始,逐次增大,当子集合大小最终和集合大小相同时,排序完毕。
3直接插入排序算法的时间复杂度:
最好:O(n)
最坏:O(n^2)
随机:O(n^2)
原始数据越接近有序,直接插入排序算法的时间效率越高,其时间效率在O(n)与O(n^2)之间
4.直接插入排序的空间复杂度为O(1),直接插入排序是一种稳定的排序算法
*/
//直接插入排序算法
void InsertSort(DataType a[],int n){
//用直接插入法对数组元素a[0]-a[n-1]进行排序
int i,j;
DataType temp;
for(i=0;i<n-1;i++){
temp=a[i+1];
j=i;
while(j>-1&&temp.key<a[j].key){
a[j+1]=a[j];
j--;
}
a[j+1]=temp;
}
}
/*
1.希尔排序的基本思想:把待排序的数据元素分成若干个小组,对同一小组内的数据元素用直接插入法排序;
小组的个数逐次减少;当完成所有数据元素都在一个组内的排序后,排序过程结束。希尔排序又称作缩小增量排序。
2.希尔排序算法的时间复杂度较直接插入排序算法的时间复杂度改善很多
3.研究表明若增量取值较合理,则希尔排序算法的时间复杂度约为O(n(lbn)^2)
4.希尔排序算法的空间复杂度为O(1),由于希尔排序是增量分组排序,所以希尔排序算法是一种不稳定的排序算法
*/
//希尔排序的算法函数
void ShellSort(DataType a[],int n,int d[],int numOfD){
//用希尔排序法对元素a[0]到a[n-1]排序,d[0]--d[numOfD-1]为希尔增量值
int i,j,k,m,span;
DataType temp;
for(m=0;m<numOfD;m++){//共numOfD次循环
span=d[m];//取本次的增量值
for(k=0;k<span;k++){//共span个小组
//组内是直接插入排序,区别是,每次不是增加1而是span
for(i=k;i<n-span;i=i+span){
temp=a[i+span];
j=i;
while(j>-1&&temp.key<a[j].key){
a[j+span]=a[j];
j=j-span;
}
a[j+span]=temp;
}
}
}
}
/*
1.选择排序的基本思想:每次从待排序的数据元素集合中选取关键字最小(或最大)的数据元素放到数据元素集合
的最前面(或最后面),数据元素集合不断缩小,当数据元素集合为空时,选择排序结束。常用的选择排序有直接选择排序
和堆排序2种。堆排序是一种基于完全二叉树的排序。
*/
/*
1.直接选择排序的基本思想:从待排序的数据元素集合中选取关键字最小的数据元素并将它与原始数据元素集合中的第一个数据元素交换位置;
然后从不包含第一个位置上数据元素的集合中选取关键字最小的数据元素,并将它与原始数据元素集合中的第二个数据元素交换;如此重复,
直到数据元素集合中只剩下一个数据元素为止。
2.直接选择排序的算法的时间复杂度为O(n^2),空间复杂度为O(1),直接选择排序是不稳定的排序算法。
*/
//直接选择排序的算法如下
void SelectSort(DataType a[],int n){
//用直接选择排序法对数组元素a[0]--a[n-1]进行排序
int i,j,small;
DataType temp;
for(i=0;i<n-1;i++){
small=i;//设第i个数据元素关键字最小
for(j=i+1;j<n;j++){//寻找关键字最小的数据元素
if(a[j].key<a[small].key){
small=j;//记住最小的下标
}
}
if(small!=i){//当最小元素的下标不为i时交换位置
temp=a[i];
a[i]=a[small];
a[small]=temp;
}
}
}
/*
1.堆排序分为最大堆(也称大顶堆或大根堆)和最小堆(也称小顶堆或小根堆)
2.最大堆的定义:设数组a中存放了n个数据元素,数组下标从0开始,如果当数组下标2i+1<n时有
a[i].key>=a[2i+1].key,当数组下标2i+2<n时有a[i].key>=a[2i+2].key,则这样的数据结构称为最大堆
3.最小堆的定义:设数组a中存放了n个数据元素,数组下标从0开始,如果当数组下标2i+1<n时有
a[i].key<=a[2i+1].key,当数组下标2i+2<n时有a[i].key<=a[2i+2].key,则这样的数据结构称为最小堆
4.最大堆的根结点是堆中值最大的数据元素,最小堆的根节点是堆中值最小的数据元素。对于最大堆,从根结点到每个叶节点
的路径上,数据元素组成的序列都是递减有序的;对于最小堆,从根结点到每个叶节点的路径上,数据元素组成的序列都是递增有序的。
5.堆排序的基本思想:首先把n元素的数组a初始化创建为最大堆,然后循环执行如下过程直到数组为空为止:
a.把堆顶a[0]元素(为最大元素)和当前最大堆的最后一个元素交换;
b.最大堆元素个数减1;
c.由于第a步后根节点不再满足最大堆的定义,所以调整根结点使之满足最大队的定义。
6.堆排序算法的时间复杂度为O(nlbn),堆排序的空间复杂度为O(1),堆排序是一种不稳定的排序算法
*/
void CreateHeap(DataType a[],int n,int h){
//调整非叶结点a[h]使之满足最大堆,n为数组a的元素个数
int i,j,flag;
DataType temp;
i=h;//i为要创建堆的二叉树根节点
j=2*i+1;//j为i的左孩子节点的下标
temp=a[i];
flag=0;
//沿左右孩子中值较大者重复向下筛选
while(j<n&&flag!=1){
//寻找左右孩子节点中的较大者,j为其下标
if(j<n-1&&a[j].key<a[j+1].key){
j++;
}
if(temp.key>a[j].key){//a[i].key.key>a[j].key
flag=1;//标志结束筛选条件
}else{//否则把a[j]上移
a[i]=a[j];
i=j;
j=2*i+1;
}
}
a[i]=temp;
}
void InitCreatHeap(DataType a[],int n){
//把数组元素a[0]--a[n-1]初始化创建为最大堆
int i;
for(i=(n-2)/2;i>=0;i--){
CreateHeap(a,n,i);
}
}
//堆排序算法函数如下
void HeapSort(DataType a[],int n){
//用堆排序法对数组元素a[0]--a[n-1]进行排序
int i;
DataType temp;
InitCreatHeap(a,n);//初始化创建最大堆
for(i=n-1;i>0;i--){//当前最大堆个数每次递减1
//把堆顶a[0]元素和当前最大堆的最后一个元素交换
temp=a[0];
a[0]=a[i];
a[i]=temp;
CreateHeap(a,i,0);//调整根结点满足最大堆
//注意此时二叉树根结点下标为0,子二叉树结点个数为i
}
}
/*
1.利用交换数据元素的位置进行排序的方法称作为交换排序。常用的交换排序方法有冒泡排序法和快速排序法。
快速排序法是一种分区交换排序方法
2.冒泡排序的基本思想:设数组a中存放了n个数据元素,循环进行n-1趟如下的排序过程;第一趟时,依次比较相邻两个数据元素
a[i].key 和a[i+1].key(i=0,1,2,3....n-2),若为逆序,即a[i].key >a[i+1].key,则交换两个数据元素,否则不交换,这样数值最大
的数据元素将放置在a[n-1]中;第二趟时,数据元素减1,即数据元素个数为n-1,操作方法和第一趟的类似,这样整个n个数据元素
集合中数值次大的数据元素将放置在a[n-2]中;当第n-1趟结束时,整个n个数据元素集合中次小数据元素放置在a[1]中,a[0]放置了
最小的数据元素。
3.冒泡排序的最好时间复杂度为O(n),最坏时间复杂度为O(n^2),冒泡排序的空间复杂度为O(1),冒泡排序是一种稳定的排序算法
*/
//冒泡排序的算法函数
void BubbleSort(DataType a[],int n){
//用冒泡排序法对数组元素a[0]--a[n-1]进行排序
int i,j,flag=1;
DataType temp;
for(i=1;i<n&&flag==1;i++){
flag=0;
for(j=0;j<n-i;j++){
if(a[j].key>a[j+1].key){
flag=1;
temp=a[j];
a[j]=a[j+1];
a[j+1]=temp;
}
}
}
}
/*
1.快速排序是一种二叉树结构的交换排序方法
2.快速排序算法的基本思想:设数组a中存放了n个数据元素,low为数组的低端下标,high为数组
的高端下标,从数组a中任取一个元素(通常取a[low])作为标准,调整数组a中各个元素的位置,使
排在标准元素前面元素的关键字均小于标准元素的关键字,排在标准元素后面元素的关键字均大于或等于
标准元素的关键字。这样一次过程结束后,一方面将标准元素放在未来排好序的数组中该标准元素应在的位置,
另一方面将数组中德元素以标准元素为中心分成了两个子数组,位于标准元素左边子数组中元素的关键字均
小于标准元素的关键字,位于标准元素右边子数组中元素的关键字均大于或等于标准元素的关键字。对这两个子数组
中的元素分别再进行方法类同的递归快速排序。递归算法的结束条件是high<=low,即上界下标小于或等于下界下标。
3.快速排序的最好情况下时间复杂度为O(nlbn),最坏情况下为O(n^2),快速排序的平均时间复杂度为O(nlbn);
快速排序算法空间复杂度最坏为O(n),快速排序算法是一种不稳定的排序算法
*/
//快速排序算法函数
void QuickSort(DataType a[],int low,int high){
//对数据元素a[low]--a[high]进行快速排序
int i=low,j=high;
DataType temp=a[low];//取第一个元素为标准数据元素
while(i<j){
while(i<j&&temp.key<=a[j].key){
j--;//在数组的右端扫描
}
if(i<j){
a[i]=a[j];
i++;
}
while(i<j&&a[i].key<temp.key){//在数组的左边扫描
i++;
}
if(i<j){
a[j]=a[i];
j--;
}
}
a[i]=temp;
if(low<i){
QuickSort(a,low,i-1);//对左端子集合进行归并
}
if(i<high){
QuickSort(a,j+1,high);//对右端子集合进行归并
}
}
/*
1.归并排序主要是二路归并排序
2.二路归并排序的基本思想:蛇数组a中存放了n个数据元素,初始时把他们看成是n个长度为1的有序子数组,然后
从第一个有序子数组开始,把相邻的有序子数组两两合并,得到[n/2]个长度为2的新的有序子数组(当n为奇数时,
最后一个新的有序子数组的长度为1)。对这些新的有序子数组在进行两两归并。如此反复,直到得到一个长度为n
的有序数组为止。
3.二路归并排序的时间复杂度为O(nlbn),空间复杂度为O(n),是一种稳定的排序算法,这是其他时间复杂度为O(nlbn)不稳定的
排序,这稳定是归并排序的最大的特点
*/
//一次二路归并排序算法
void Merge(DataType a[],int n,DataType swap[],int k){
//k为有序子数组的长度,一次二路归并排序后的有序子序列存于数组swap中
int m=0,u1,l2,i,j,u2;
int low=0;//第一个有序子数组下界为0
while(low+k<=n-1){
l2=low+k;//计算第二个有序数组下界
u1=l2-1;//计算第一个有序子数组上界
u2=((l2+k-1)<=(n-1))?(l2+k-1):(n-1);//计算第二个有序子数组上界
//两个有序子数组合并
for(i=low,j=l2;i<u1&&j<=u2;m++){
if(a[i].key<=a[j].key){
swap[m]=a[i];
i++;
}else{
swap[m]=a[j];
j++;
}
}
//子数组2已经归并完,将子数组1中剩余的元素存放到数组swap中
while(i<=u1){
swap[m]=a[i];
m++;
i++;
}
//子数组1已经归并完,将子数组2中剩余的元素存放到数组swap中
while(j<=u2){
swap[m]=a[j];
m++;
j++;
}
low=u2+1;
}
//将原数组中只够一组的数据元素顺序存放到数组swap中
for(i=low;i<n;i++,m++){
swap[m]=a[i];
}
}
//二路归并排序算法
void MergeSort(DataType a[],int n){
int i,k=1;//归并长度从1开始
DataType *swap;
swap=(DataType *)malloc(sizeof(DataType)*n);//申请动态数组空间
while(k<n){
Merge(a,n,swap,k);//调用归并函数Merge(DataType a[],int n,DataType swap[],int k)
for(i=0;i<n;i++){
a[i]=swap[i];//将元素从临时数组swap放回数组a中
}
k=2*k;//归并长度加倍
}
free(swap);//释放动态数组空间
}
/*
1.基数排序也称作桶排序,是一种当关键字为整数类型时非常高效的排序算法.
2.基数排序的基本思想:设待排序的数据元素关键字是m位d进制整数(不足m位的关键字在高位补0),
设置d个桶,令其编号分别为0,1,2,3,4....d-1.首先按关键字最低位的数值依次把各数据元素放到相应的桶中
,然后按照桶号从小到大和进入桶中数据元素的先后次序收集分配在各桶中的数据元素,这样就形成了数据元素集合的一个新的
排列,称这样的一次排序过程为一次基数排序;再对一次基数排序得到的数据元素序列按关键字次低位的数值
依次把各数据元素放到相应的桶中,然后按照桶从小到大和进入桶中数据元素的先后次序收集分配在各桶中的数据元素;这样的
过程重复进行,当完成了第m次基数排序后,就得到了排好序的数据元素序列。
//基于链式队列的基数排序算法的时间复杂度为O(2mn)或O(mn)
//基于链式队列的基数排序算法的空间复杂度为O(n)
//基于顺序队列的基数排序算法时间复杂度为O(2mn)或O(mn),空间复杂度为O(mn)
//基数排序是一种稳定的排序算法
*/
typedef struct qnode{//链式队列的结点
DataType data;//数据部分
struct qnode *next;//指向下一个结点
}LQNode;
typedef struct{
LQNode *front;//队头指针
LQNode *rear;//对尾指针
}LQueue;
//初始化队列
void QueueInitiate(LQueue *q){//初始化链式队列q
q->front=NULL;//定义初始队尾指针下标值
q->rear=NULL;//定义初始对头指针下标值
}
//非空否
int QueueNotEmpty(LQueue q){
//判断链式队列q非空否,非空则返回1,否则返回0
if(q.front==NULL){
return 0;
}else{
return 1;
}
}
//入队列
void QueueAppend(LQueue *q,DataType x){
//把数据元素值x插入链式队列q的队尾
LQNode *p;
p=(LQNode *)malloc(sizeof(LQNode));
p->data=x;
p->next=NULL;
if(q->rear!=NULL){//队列原来非空时队尾加新结点
q->rear->next=p;
}
q->rear=p;//修改队尾指针
if(q->front==NULL){//队列原来为空时修改对头指针
q->front=p;
}
}
//出队列
int QueueDelete(LQueue *q,DataType *d){
//删除链式队列q的对头数据元素值d,出队列成功返回1,否则返回0
LQNode *p;
if(q->front==NULL){
printf("队列已空无数据元素出队列!!\n");
return 0;
}else{
*d=q->front->data;
p=q->front;
q->front=q->front->next;//出队列结点脱链
if(q->front==NULL){
q->rear=NULL;
}
free(p);
return 1;
}
}
//取对头数据元素
int QueueGet(LQueue q,DataType *d){
//取链式队列q的队头数据元素到d,成功返回1,否则返回0
if(q.front==NULL){
printf("队列已空无数据元素出队列!!\n");
return 0;
}else{
*d=q.front->data;
return 1;
}
}
//撤销动态申请空间
void Destroy(LQueue q){
LQNode *p,*p1;
p=q.front;
while(p!=NULL){
p1=p;
p=p->next;
free(p1);
}
}
//基数排序
//基于链式队列的基数排序算法的时间复杂度为O(2mn)或O(mn)
//基于链式队列的基数排序算法的空间复杂度为O(n)
//基于顺序队列的基数排序算法时间复杂度为O(2mn)或O(mn),空间复杂度为O(mn)
//基数排序是一种稳定的排序算法
void RadixSort(DataType a[],int n,int m,int d){
//对数据元素a[0]--a[n-1]进行关键字为m位d进制整形数值的基数排序
//桶采用链式队列
int i,j,k,power=1;
LQueue *tub;
//把d个队列定义为动态数组
tub=(LQueue *)malloc(sizeof(LQueue)*d);
for(i=0;i<d;i++){
QueueInitiate(&tub[i]);//d的队列分别初始化
}
//进行m次放和收
for(i=0;i<m;i++){
if(i==0){
power=1;
}else{
power=power*d;
}
//将数据元素按关键字第k位的数值放到相应的队列中
for(j=0;j<n;j++){
k=a[j].key/power-(a[j].key/(power*d))*d;//计算k
QueueAppend(&tub[k],a[j]);//把a[j]放入第k个队列中
}
//顺序回收各队列中的数据元素至数组a中
k=0;
for(j=0;j<d;j++){
while(QueueNotEmpty(tub[j])!=0){
QueueDelete(&tub[j],&a[k]);//从各队列中回收
k++;
}
}
}
}
void main(){
DataType test[6]={64,5,7,89,6,24};
DataType b[]={65,34,25,87,12,38,56,46,14,77,92,23};
DataType c[]={88,76,40,50,10,9,32,5};
int d[]={6,3,1};
int i,n=6;
//直接插入排序测试
InsertSort(test,n);
printf("直接插入排序: ");
for(i=0;i<n;i++){
printf("%d ",test[i].key);
}
printf("\n");
//希尔测试
ShellSort(b,12,d,3);
printf("希尔排序: ");
for(i=0;i<12;i++){
printf("%d ",b[i].key);
}
printf("\n");
//直接选择排序
SelectSort(test,n);
printf("直接选择排序: ");
for(i=0;i<n;i++){
printf("%d ",test[i].key);
}
printf("\n");
//堆排序
HeapSort(c,8);
printf("堆排序: ");
for(i=0;i<8;i++){
printf("%d ",c[i].key);
}
printf("\n");
//冒泡排序
BubbleSort(test,n);
printf("冒泡排序: ");
for(i=0;i<n;i++){
printf("%d ",test[i].key);
}
printf("\n");
//快速排序
DataType quick[]={98,78,67,3,2,56,44,55,48,88};
QuickSort(quick,0,9);
printf("快速排序: ");
for(i=0;i<10;i++){
printf("%d ",quick[i].key);
}
printf("\n");
//归并排序
DataType meger[]={72,73,71,23,94,16,5,68,64};
//归并排序有点bug一下没找到
MergeSort(meger,9);
printf("归并排序: ");
for(i=0;i<9;i++){
printf("%d ",meger[i].key);
}
printf("\n");
//基数排序
DataType jishu[]={710,342,45,686,6,841,429,134,68,246};
RadixSort(jishu,10,3,10);
printf("基数排序: ");
for(i=0;i<10;i++){
printf("%d ",jishu[i].key);
}
printf("\n");
}
排序结果: