排序算法很多,我把我遇到的所有都写出来放在这.
插入排序,冒泡排序,选择排序...这些简单一点的不啰嗦了.
&:堆排序
堆排序,用数组实现,首先要了解堆的性质.
下面的堆排序,按非递减顺序,由三部分构成:
1.MAX_HEAPIFY(A[],i),用于维护最大堆的性质,它假定以 i 为根节点的左右两个子树都是最大堆,然后在和 i 合并后依然是最大堆,即若A[i]小于它的孩子就下移;
2.BUILD_MAX_HEAP(A[]),只要从最后一个节点的父亲开始调用MAX_HEAPIFY(A[],i)一直向前到根节点就行了,因为后面的都是叶子节点,已经维持了最大堆的性质.
3.HEAP_SORT(A[]),最后开始堆排序,首先建堆,然后每次把根节点(即最大的)和最后一个节点交换,堆长度-1,再在根处维持最大堆性质,即调用MAX_HEAPIFY(A[],i),则又是最大堆.
/*堆排序*/ #include <stdio.h> #define MAXLEN 1000 int arr[MAXLEN]; void Max_Heapify(int A[],int length,int i) //传入数组的指针,数组的长度,该点的下标,维持最大堆的性质 { int L_Index=i*2; int R_Index=i*2+1; int larger_index=i; //默认它们三个中最大的i if (L_Index<length&&A[L_Index]>A[i]) //注意不能超多length,下标从0开始的 { larger_index=L_Index; } if (R_Index<length&&A[R_Index]>A[larger_index]) { larger_index=R_Index; } if (larger_index!=i) //i不是它们之中的最大的时候 { /*先交换i与最大的*/ int temp=A[i]; A[i]=A[larger_index]; A[larger_index]=temp; Max_Heapify(A,length,larger_index); //递归地维持此性质,可能还要下移 } } void Build_Max_Heap(int A[],int length) //构建最大堆 { for (int i=(length-1)/2;i>=0;i--) //注意i的起始地方,最后的上面一个 { Max_Heapify(A,length,i); } } void Heap_Sort(int A[],int length) { Build_Max_Heap(A,length); //先建堆 for (int i=length-1;i>0;i--) //注意i的起始 { /*交换堆顶和最后一个元素*/ int temp=A[0]; A[0]=A[i]; A[i]=temp; length=length-1; //长度减1 Max_Heapify(A,length,0); //在根节点处维持最大堆性质 } } int main(void) { int temp,len=0; while (scanf_s("%d",&temp)!=EOF) { arr[len++]=temp; } puts("输入如下:"); for (int i=0;i<len;i++) { printf("%d ",arr[i]); } putchar('\n'); Heap_Sort(arr,len); puts("按非减顺序排序如下:"); for (int i = 0; i < len; i++) { printf("%d ",arr[i]); } getchar(); return 0; }
&:归并排序
归并排序,用到了分治的思想,即要排序一个数组,把它递归地分成两个部分,排序这两个部分,然后合并.可以看做由部分到整体.关键是递归.当划分成一个或者0个元素时,则解决了子问题,然后合并子问题,最终得到解,注意Merge()过程.
View Code /*分治法---归并排序*/ #include <stdio.h> #define MAXLEN 1000 int arr[MAXLEN]; int L[MAXLEN]; //在合并两个数组的时候用,用来装数组 int R[MAXLEN]; void Merge_Sort(int a[],int s,int e); //归并排序,传进数组的指针和起始,终止下标,按非递减顺序 void Merge(int a[],int s,int m,int e); //归并两个数组,即数组a[]中下标为[s...m]和[m+1...e] int main(void) { int temp,len=0; while (scanf_s("%d",&temp)!=EOF) { arr[len++]=temp; } for (int i=0;i<len;i++) { printf("%d ",arr[i]); } putchar('\n'); Merge_Sort(arr,0,len-1); for (int i=0;i<len;i++) { printf("%d ",arr[i]); } getchar(); return 0; } void Merge_Sort(int a[],int s,int e) { if (s<e) //如果两个相等,则只有一个元素,则已经排好序 { int m=(s+e)/2; Merge_Sort(a,s,m); Merge_Sort(a,m+1,e); Merge(a,s,m,e); } } void Merge(int a[],int s,int m,int e) { int len1=m-s+1; int len2=e-m; for (int i=0;i<len1;i++) //左边的数组 { L[i]=a[s+i]; } for (int i=0;i<len2;i++) //右边数组 { R[i]=a[m+1+i]; } int i=0,j=0,k=s; //分别是左,右和结果数组的游标 while (i<len1&&j<len2) { if (L[i]<=R[j]) { a[k++]=L[i++]; } else { a[k++]=R[j++]; } } /*处理剩下的*/ while(i<len1) //左边数组还有剩余 { a[k++]=L[i++]; } while (j<len2) { a[k++]=R[j++]; } }
&:快速排序
快速排序用到了分治的思想,不同于归并排序,它好似是从整体到部分,先选一个主元,第一次排序的结果是主元大概在数组的中间,左边都是<=主元的,右边都是>主元的,然后一直这样递归,直到每个小部分都有序了,则整体也有序了.
int Partition(A[],p,r),这个函数实现了对A[p,r]的原址重排,实现了主元左边都小于右边,返回主元的位置
/*快速排序*/ #include <stdio.h> #define MAXLEN 1000 int arr[MAXLEN]; int Partition(int A[],int p,int r) //传进数组指针和子数组的起始和终止位置 { int x=A[r]; //选r位置为主元,当然也可随机化地选择 int i=p-1; int j=p; for (;j<r;j++) //遍历数组每个元素,除了最后一个,每次循环的开始 [p,i]<=x [i+1,j-1]>x { if (A[j]<=x) //若果小于,则把它放到小于区域,如果大于,则大于区域增加了,在for循环里面加的 { i++; //小于去先增加一个 /*交换两个值*/ int temp=A[i]; A[i]=A[j]; A[j]=temp; } } /*把大的里面的第一个和主元交换,则主元到了大概中间位置*/ int temp=A[r]; A[r]=A[i+1]; A[i+1]=temp; return i+1; //返回主元位置 } void Quick_Sort(int A[],int p,int r) //快排,传进数组地址和起始终止下标 { if (p<r) //若至少有两个,则需要排序,当为0个或一个时,已经排好序了 { int q=Partition(A,p,r); //排成主元左半小于右半 Quick_Sort(A,p,q-1); //排左半 Quick_Sort(A,q+1,r); //排右半 } } int main(void) { int temp,len=0; while (scanf_s("%d",&temp)!=EOF) { arr[len++]=temp; } puts("输入如下:"); for (int i=0;i<len;i++) { printf("%d ",arr[i]); } putchar('\n'); Quick_Sort(arr,0,len-1); puts("按非减顺序排序如下:"); for (int i = 0; i < len; i++) { printf("%d ",arr[i]); } getchar(); return 0; }
&:计数排序
假设输入的每个数都是[0,k]之间的整数,对于每个输入数,先统计小于这个数的个数,利用此就可以直接把该数放到输出数组中的位置了.
输入数组:A[],n个元素, 0<=A[i]<=k ; 输出数组为B[],n个元素; 还要一个临时数组C[0,k],首先,把A[]中各个元素出现的次数放到C[]对应的下标,然后通过加得到C[]每个下标对应的数应该放在B[]的那个位置.
这是非比较排序.
/*计数排序*/ #include <stdio.h> #include <string.h> #define MAXLEN 100 //待排序数个数 #define MAXNUM 1000 //最大数 void Counting_Sort(int A[],int B[],int n) //传进来原来数组指针A,结果放到B[],n个数,每个数的范围是 [0,MAXNUM] { int C[MAXNUM+1]; //保证最大数也要有下标 memset(C,0,sizeof(C)); /*先对C[]第一次操作,得每个数出现的次数*/ for (int i=0;i<n;i++) //对A[]的每个数, { C[A[i]]++; } /*对C[]第二次操作,得每个数在B[]的位置*/ for (int i=1;i<=MAXNUM;i++) //注意这里的i从1开始,这样的话输出到B[]时就要减一了 { C[i]=C[i]+C[i-1]; } /*下面输出结果到B[]*/ for (int i=n-1;i>=0;i--) //从A[]的角度出发,倒着来的-----保证了稳定性 { B[C[A[i]]-1]=A[i]; //把A[]中每个元素放到B[],通过C[]找到位置 C[A[i]]--; //减一,如果有多个相同的,还保证了稳定性 } } int main(void) { int A[MAXLEN]; int B[MAXLEN]; int temp,len=0; while (scanf_s("%d",&temp)!=EOF) { A[len++]=temp; } puts("输入如下:"); for (int i=0;i<len;i++) { printf("%d ",A[i]); } putchar('\n'); Counting_Sort(A,B,len); //假设每个数小于等于1000 puts("按非减顺序排序如下:"); for (int i = 0; i < len; i++) { printf("%d ",B[i]); } getchar(); return 0; }
&:基数排序
本来是用在卡片排序机上的算法,(太嫩了,没见过).讲起来很简单,假设每个数字或者字符什么的有d位,先按最低(当然按从高到低也行,但是当初的情形是这样的)有效位排序(这里的排序随便用什么都行,但要求必须是稳定的排序方法---即具有相同值的元素在输出数组中的相对次序必须与它们在输入数组中的相对次序相同,想一下最低位不同但是次低位相同的情况),这个排序是稳定的,再按次低有效位进行和同样的操作,一直到按最高位排序,排完了,得到的也就是有序了.
/*基数排序*/ #include <stdio.h> #define MAXLEN 100 //待排序数个数最大值 void Radix_Sort(int A[],int n,int d) //数组指针,数组元素个数,位数的最大值 { for (int i=1;i<=d;i++) //对每个位进行一次排序 { /*这里就用选择排序对每一位排序吧---是稳定的*/ for (int j=0;j<n-1;j++) { for (int k=j+1;k<n;k++) { int a_temp=A[j]; int b_temp=A[k]; /*下面获得相应的第i低位*/ int a=0,b=0; for (int m=1;m<=i;m++) //对上面两个数分别除i次 { a=a_temp%10; b=b_temp%10; a_temp/=10; b_temp/=10; } /*根据第i低位排序*/ if (b<a) { int tem=A[k]; A[k]=A[j]; A[j]=tem; } } } } } int main(void) { int A[MAXLEN]; int temp,len=0; while (scanf_s("%d",&temp)!=EOF) { A[len++]=temp; } puts("输入如下:"); for (int i=0;i<len;i++) { printf("%d ",A[i]); } putchar('\n'); Radix_Sort(A,len,3); //假设最多是3位数 puts("按非减顺序排序如下:"); for (int i = 0; i < len; i++) { printf("%d ",A[i]); } getchar(); return 0; }
&:桶排序
就是将待排序的元素分成n个相同大小的子区间,每个区间就称为桶,然后将所有的待排序元素放入对应的桶的对应的位置,最后按桶依次取出来,即排好序了.
下面的代码是将n个全部小写字符串进行桶排序,输入为 char A[n][],n个字符串,还需一个临时的"桶"B[26],a,b,c,d,...每个都是一个桶,对于每个桶,用链表装排好序的字符串的指针.
/*桶排序*/ #include <stdio.h> #include <string.h> #include <stdlib.h> #define MAXLEN 100 //待排序数个数最大值 typedef struct node //桶里面的每一个元素都这样装,其实装的只是字符串的指针 { char * p; //这个指向对应的字符串 struct node * next; //指向下一个节点 }Node; void Bucket_Sort(char a[][11],int n,char * c[]) //桶排序,传进字符串指针和总个数和装结果的指针 { /*初始化桶*/ Node * B[26]; //创建一个桶,B[]即为桶,用下标对应每个小写字符 for (int i=0;i<26;i++) //清空桶 { B[i]=NULL; } /*下面把每个字符串装桶里面去*/ for (int i=0;i<n;i++) { char temp=a[i][0]; //获得这个字符串的首字母,按首字母进桶 int index=temp-97; //首字母对应B[]中的下标 if (B[index]) //已经至少有一个放这个桶里面了,找到合适位置放了,这里其实就是链表插入 { Node * pNode=(Node *)malloc(sizeof(Node)); pNode->p=a[i]; /*下面找到正确的插入位置*/ Node * temp=B[index]; //指向第一个 Node * pre=NULL; //记录前一个指针,最后就在它所指向的节点后面加pNode while (temp) { if (strcmp(a[i],temp->p)>0) //按从小到大顺序,大于就后移 { pre=temp; temp=temp->next; } else { break; //跳出 } } if (pre) //非空,在所指之后加 { pNode->next=pre->next; pre->next=pNode; } else //在第一个之前加 { pNode->next=B[index]; B[index]=pNode; } } else //这个桶里的第一个 { B[index]=(Node *)malloc(sizeof(Node)); if (!B[index]) { exit(1); } B[index]->p=a[i]; B[index]->next=NULL; } } /*把桶里面的排序结果取出综合到输出指针数组*/ int toIndex=0; for (int i=0;i<26;i++) //对每个桶 { Node * temp=B[i]; while (temp) { c[toIndex]=temp->p; toIndex++; temp=temp->next; } } } int main(void) { char A[MAXLEN][11]; //输入假设每个字符串最长为10,多一个用来放空字符 char * C[MAXLEN]; //C放的是排好序后的数组,只是放的是指针,字符串在A[][]里面,A[]输入了以后就没有再动了 int len=0; while (gets_s(A[len])) { len++; } puts("输入如下:"); for (int i=0;i<len;i++) { puts(A[i]); } Bucket_Sort(A,len,C); puts("按非减顺序排序如下:"); for (int i = 0; i < len; i++) { puts(C[i]); } getchar(); return 0; }
附:
http://blog.csdn.net/johnny710vip/article/details/6895654(排序算法稳定性,讲得很好)