各类排序算法
堆排序
堆排序利用了大根堆(或小根堆)堆顶记录的关键字最大(或最小)这一特征,使得在当前无序区中选取最大(或最小)关键字的记录变得简单。
(1)用大根堆排序的基本思想
① 先将初始文件R[1..n]建成一个大根堆,此堆为初始的无序区
② 再将关键字最大的记录R[1](即堆顶)和无序区的最后一个记录R[n]交换,由此得到新的无序区R[1..n-1]和有序区R[n],且满足R[1..n-1].keys≤R[n].key
③由于交换后新的根R[1]可能违反堆性质,故应将当前无序区R[1..n-1]调整为堆。然后再次将R[1..n-1]中关键字最大的记录R[1]和该区间的最后一个记录R[n-1]交换,由此得到新的无序区R[1..n-2]和有序区R[n-1..n],且仍满足关系R[1..n-2].keys≤R[n-1..n].keys,同样要将R[1..n-2]调整为堆。
……
直到无序区只有一个元素为止。
(2)大根堆排序算法的基本操作:
① 初始化操作:将R[1..n]构造为初始堆;
② 每一趟排序的基本操作:将当前无序区的堆顶记录R[1]和该区间的最后一个记录交换,然后将新的无序区调整为堆(亦称重建堆)。
注意:
①只需做n-1趟排序,选出较大的n-1个关键字即可以使得文件递增有序。
②用小根堆排序与利用大根堆类似,只不过其排序结果是递减有序的。堆排序和直接选择排序相反:在任何时刻堆排序中无序区总是在有序区之前,且有序区是在原向量的尾部由后往前逐步扩大至整个向量为止
特点
堆排序(HeapSort)是一树形选择排序。堆排序的特点是:在排序过程中,将R[l..n]看成是一棵完全二叉树的顺序存储结构,利用完全二叉树中双亲结点和孩子结点之间的内在关系(参见二叉树的顺序存储结构),在当前无序区中选择关键字最大(或最小)的记录
堆排序的最坏时间复杂度为O(nlogn)。堆序的平均性能较接近于最坏性能。
初始建堆的时间约为n/2*logn, 最初从n/2即倒数第二层开始调整,使得当前节点为孩子和自己中的最小(大)值。那么调整到第一层时,根节点为最小值。
不稳定的排序算法
代码
1 void adjust(int i,int n)//调整为小顶堆 2 { 3 int j = i,k; 4 a[0] = a[i]; 5 j = 2*i; 6 while(j<=n) 7 { 8 if(j < n&&a[j]>a[j+1])//左右孩子结点的最小值 9 j++; 10 if(a[0]>a[j])//如果比最小值大 11 { 12 a[i] = a[j];//当前最小值孩子结点上移 i变为当前j值 继续与下面比较 13 i = j; 14 j = 2*j; 15 } 16 else 17 break; 18 } 19 a[i] = a[0];//放在合适位置 20 } 21 void heapsort(int n) 22 { 23 int i,t; 24 for(i = n/2 ; i>0 ; i--)//最初调整为堆 25 adjust(i,n); 26 for(i = n ; i>1 ; i--)//依次交换根节点和尾结点 27 { 28 t = a[1]; 29 a[1] = a[i]; 30 a[i] = t; 31 adjust(1,i-1); 32 } 33 }
快速排序
设要排序的数组是A[0]……A[N-1],首先任意选取一个数据(通常选用第一个数据)作为关键数据,然后将所有比它小的数都放到它前面,所有比它大的数都放到它后面,这个过程称为一趟快速排序。值得注意的是,快速排序不是一种稳定的排序算法,也就是说,多个相同的值的相对位置也许会在算法结束时产生变动。
最坏 时间复杂度为o(n2)。
最佳 T(n)=θ(nlogn)
代码
1 /* 2 一趟快速排序的算法是: 3 找一个记录,以它的关键字作为“枢轴”, 4 凡其关键字小于枢轴的记录均移动至该记录之前, 5 反之,凡关键字大于枢轴的记录均移动至该记录之后。 6 A[0] A[1] A[2] A[3] A[4] A[5] A[6]: 7 49 38 65 97 76 13 27 8 进行第一次交换后: 9 27 38 65 97 76 13 49 10 进行第二次交换后: 11 27 38 49 97 76 13 65 12 进行第三次交换后:27 38 13 97 76 49 65 13 进行第四次交换后:27 38 13 49 76 97 65 14 */ 15 int fqsort(int low,int high,int a[]) 16 { 17 int i = low,j = high,k = a[low];//找一个比较的点 18 while(i<j) 19 { 20 while(i<j&&a[j]<=k)//从右向左移直至找到比k大的数 21 j--; 22 a[i] = a[j];//比这个数大的放左边 23 while(i<j&&a[i]>=k)//从左向右移直至找到比k小的数 24 i++; 25 a[j] = a[i];//比这个数大的放右边 26 } 27 a[i] = k; 28 return j;//j左右两边已经被k分割开 再把j+1当作右边一组的low j-1当作左边一组的high进行下一次的快排 29 } 30 void qsort(int low,int high,int a[]) 31 { 32 int q; 33 if(low<high) 34 { 35 q = fqsort(low,high,a);//找到分割点 36 qsort(low,q-1,a);//左右两边进行快排 37 qsort(q+1,high,a); 38 } 39 }
归并排序
归并排序是建立在归并操作上的一种有效的排序算法。该算法是采用分治法(Divide and Conquer)的一个非常典型的应用。
将已有序的子序列合并,得到完全有序的序列;即先使每个子序列有序,再使子序列段间有序。若将两个有序表合并成一个有序表,称为2-路归并。
归并排序是稳定的排序.即相等的元素的顺序不会改变.如输入记录 1(1) 3(2) 2(3) 2(4) 5(5) (括号中是记录的关键字)时输出的 1(1)
2(3) 2(4) 3(2) 5(5) 中的2 和 2
是按输入的顺序.这对要排序数据包含多个信息而要按其中的某一个信息排序,要求其它信息尽量按输入的顺序排列时很重要.这也是它比快速排序优势的地方.
代码
1 void msort(int low,int mid,int high)//将两部分归并 2 { 3 int i,j,n1 = mid-low+1,n2 = high-mid,x[10001],y[10001]; 4 for(i = 1 ; i <= n1 ; i++)//存于两个数组中 5 x[i] = a[low+i-1]; 6 for(i = 1 ; i <= n2 ; i++) 7 y[i] = a[mid+i]; 8 int k = low; 9 i = 1; 10 j = 1; 11 while(i<=n1&&j<=n2)//排序 12 { 13 if(x[i]<y[j]) 14 a[k] = x[i++]; 15 else 16 a[k] = y[j++]; 17 k++; 18 } 19 while(i<=n1) 20 a[k++] = x[i++]; 21 while(j<=n2) 22 a[k++] = y[j++]; 23 } 24 void merge(int low,int high)//二分递归合并 25 { 26 int mid; 27 if(low<high) 28 { 29 mid = (low+high)/2; 30 merge(low,mid); 31 merge(mid+1,high); 32 msort(low,mid ,high); 33 } 34 }
拓扑排序
拓扑排序是对有向无环图的一种排序。表示了顶点按边的方向出现的先后顺序。如果有环,则无法表示两个顶点的先后顺序。
http://acm.hdu.edu.cn/showproblem.php?pid=1285
以这道题为例
这题说明了 不会出现环 就没判断
非递归版
1 #include<stdio.h> 2 #include<string.h> 3 int g[501][501],c[501],topo[501],de[501]; 4 void toposort(int n) 5 { 6 int i,j,k; 7 for(i = 1 ;i <= n ;i++)//n次查找 8 { 9 for(j = 1 ; j <= n ; j++) 10 { 11 if(de[j]==0)//寻找入度为0的节点 并将其删除 12 { 13 de[j]--; 14 topo[i] = j; 15 for(k = 1 ;k <= n ; k++)//同时更新以它为前驱节点的入度 16 if(g[j][k]) 17 de[k]--; 18 break; 19 } 20 } 21 } 22 } 23 int main() 24 { 25 int n,m,i,j,a,b; 26 while(scanf("%d%d", &n,&m)!=EOF) 27 { 28 memset(g,0,sizeof(g)); 29 memset(de,0,sizeof(de)); 30 for(i = 1 ;i <= m ; i++) 31 { 32 scanf("%d%d",&a,&b); 33 if(!g[a][b]) 34 { 35 g[a][b] = 1; 36 de[b]++; 37 } 38 } 39 toposort(n); 40 for(i = 1 ;i < n ; i++) 41 printf("%d ",topo[i]); 42 printf("%d\n",topo[n]); 43 } 44 return 0; 45 }
dfs 递归回溯版
1 int dfs(int u,int n) 2 { 3 int v; 4 c[u] = -1; 5 for(v = 1 ; v <= n ; v++) 6 if(g[u][v]) 7 { 8 if(c[v]<0) 9 { 10 return 0; 11 } 12 else 13 if(!c[v]&&!dfs(v,n)) 14 return 0; 15 } 16 c[u] = 1; 17 return 1; 18 } 19 int toposort(int n) 20 { 21 int u; 22 memset(c,0,sizeof(c)); 23 for(u = 1 ;u <= n ;u++) 24 if(!c[u]) 25 { 26 if(!dfs(u,n)) 27 return 0; 28 } 29 return 1; 30 }