【模板】常用排序
来源:互联网 发布:淘宝卖家如何借贷 编辑:程序博客网 时间:2024/06/07 01:16
我的模板:
#include<stdio.h>#include<iostream>using namespace std;//print(a,len);void print(int a[], int n ){ for(int j= 0; j<n; j++) cout<<a[j] <<" "; cout<<endl;}//1.插入排序void InsertSort(int *a,int len)//数组从a[0]开始存!{ for(int i=1; i<len; ++i) { int j=i,x=a[i]; while(j&&a[j-1]>x) a[j]=a[j-1],j--; a[j]=x; }}//2.希尔插入=缩小增量排序void ShellInsert(int a[], int n, int d)//数组从a[0]开始存!{ for(int i= d; i<n; ++i) { if(a[i] < a[i-d]) { int j = i,x = a[i]; while(j&& a[j-d]>x) { a[j] = a[j-d]; j -= d; } a[j] = x; } }}void ShellSort(int*a,int len){ int d=len; while (d>1) { d=d/2; ShellInsert(a,len,d); }}//3.选择排序(二元)void SelectSort(int* R,int n){ int i,j,k; for(i=1; i<n; i++) //做第i趟排序(1≤i≤n-1) { k=i; for(j=i+1; j<=n; j++) //在当前无序区R[i‥n]中选key最小的记录R[k] if(R[j]<R[k]) k=j; //k记下目前找到的最小关键字所在的位置 if(k!=i) // //交换R[i]和R[k] { R[0]=R[i]; R[i]=R[k]; R[k]=R[0]; } print(R,n); //endif } //endfor}//4.堆排序void Heapadjust(int *a ,int root,int len)//堆调整 { int child, x=a[root]; while(child=root<<1,child<len) { if(child+1 <len && a[child]<a[child+1]) ++child ; if(x<a[child]) { a[root]=a[child]; root=child; child++; } else break; } a[root]=x;}void HeapSort(int *a,int len){ for(int i=(len-1)/2; i>=0; i--) Heapadjust(a,i,len); while(--len) { swap(a[0],a[len]); Heapadjust(a,0,len); }}//5.冒泡排序(略)//6.快速排序void quicksort(int a[], int left, int right){ if(left >= right) return ; int i = left, j = right; int flag = a[left];//基准 do { while(a[j]>flag && i<j)//一定要从右开始减!! j--; if(i < j) { a[i] = a[j]; i++; } while(a[i]<flag && i<j) i++; if(i < j) { a[j] = a[i]; j--; } } while(i != j); a[i] = flag;//放下基准,坐标为i quicksort(a, left, i - 1); quicksort(a, i + 1, right);}//7.归并排序void arry_add(int arry[], int left, int mid, int right)//不需辅助空间{ if(left >= right) return ; int i = left, j = mid + 1, k = 0; while(i <= mid && j <= right) { if(arry[i] <= arry[j]) tmp[k++] = arry[i++]; else { tmp[k++] = arry[j++]; cnt += (mid - i + 1); } } while(i <= mid) tmp[k++] = arry[i++]; while(j <= right) tmp[k++] = arry[j++]; for(i = 0; i < k; i++) arry[i + left] = tmp[i];}void merge_sort2(int arry[], int left, int right){ if(left >= right) return ; int mid = (left + right) >> 1; merge_sort(arry, left, mid); merge_sort(arry, mid + 1, right); arry_add(arry, left, mid, right);}void merge_sort1(int* a, int left, int right, int* tem)//左闭右开{ if(right-left > 1) { int mid = left + (right-left)/2; //划分 int p = left, q = mid, t = left; merge_sort(a, left, mid, tem);//递归求解 merge_sort(a, mid, right, tem);//递归求解 while(p < mid || q < right) { if(q >= right || (p < mid && a[p] <= a[q])) tem[t++] = a[p++]; //从左半数组复制到临时空间 else tem[t++] = a[q++]; //从右半数组复制到临时空间 } for(int i = left; i < right; ++i) a[i] = tem[i]; //从辅助空间复制回a数组 }}//8.基数排序(略)int main(){ int a[10]= {56,0,5,9,4,17,55,6,1,89}; int len=10; quick_sort(a,0,9); print(a,len); return 0;}
过程:
一、基本概念:
1、 排序:按照一定的关键字,将一个序列排列成想要得到的一个新的序列。
2、 内部排序和外部排序:整个排序过程完全在内存中进行,叫做内部排序。数据量较大需要借助外部存储设备才能完成,叫做外部排序。
3、 主关键字和次关键字
4、 排序的稳定性
二、插入类排序:
(一) 思想:在一个已经排好序的序列中,将未被排进的元素按照原先的规定插入到指定位置。
(二) 分类:
1、 直接插入排序:
① 思想:最基本的插入排序,将第i个插入到前i-1个中的适当位置。
② 时间复杂度:T(n) = O(n²)。
③ 空间复杂度:S(n) = O(1)。
④ 稳定性:稳定排序。循环条件while(r[0].key < r[j].key)保证的。
⑤ 程序:
void InsertSort(int a[], int n){ for(int i= 1; i<n; i++) { if(a[i] < a[i-1]) //若第i个元素大于i-1元素,直接插入。小于的话,移动有序表后插入 { int j= i-1; int x = a[i]; //复制为哨兵,即存储待排序元素 a[i] = a[i-1]; //先后移一个元素 while(x < a[j]) //查找在有序表的插入位置 { a[j+1] = a[j]; j--; //元素后移 } a[j+1] = x; //插入到正确位置 } }}
2、 折半插入排序:
① 思想:因为是已经确定了前部分是有序序列,所以在查找插入位置的时候可以用折半查找的方法进行查找,提高效率。
② 时间复杂度:比较时的时间减为O(n㏒n),但是移动元素的时间耗费未变,所以总是得时间复杂度还是O(n²)。
③ 空间复杂度:S(n) = O(1)。
④ 稳定性:稳定排序。
⑤ 程序:
void BinSort(RecordType r[], int length) { for(i = 2; i <= length; i++) { x = r[i]; low = 1; high = i – 1; while(low <= high) { mid = (low + high) / 2; if(x.key < r[mid].key) high = mid – 1; else low = mid – 1; } for(j = i – 1; j >= low; --j) r[j + 1] = r[j]; r[low] = x; } }
3、 希尔排序:
① 思想:又称缩小增量排序法。把待排序序列分成若干较小的子序列,然后逐个使用直接插入排序法排序,最后再对一个较为有序的序列进行一次排序,主要是为了减少移动的次数,提高效率。原理应该就是从无序到渐渐有序,要比直接从无序到有序移动的次数会少一些。
② 时间复杂度:O(n的1.5次方)
③ 空间复杂度:O(1)
④ 稳定性:不稳定排序。{2,4,1,2},2和1一组4和2一组,进行希尔排序,第一个2和最后一个2会发生位置上的变化。
⑤ 程序:
void ShellInsert(RecordType r[], int length, int delta) { for(i = 1 + delta; i <= length; i++)/*1+delta为第一个子序列的第二个元素的下表*/ if(r[i].key < r[1 - delta].key) { r[0] = r[i]; for(j = i – delta; j > 0 && r[0].key < r[j].key; j -=delta) r[j + delta] = r[j]; r[j + delta] = r[0]; } } void ShellSort(RecordType r[], int length, int delta[], int n) { for(i = 0; i <= n – 1; ++i) ShellInsert(r, length, delta[i]); }
三、交换类排序:
(一) 思想:通过交换逆序元素进行排序的方法。
(二) 分类:
1、 冒泡排序:
① 思想:反复扫描待排序序列,在扫描的过程中顺次比较相邻的两个元素的大小,若逆序就交换位置。第一趟,从第一个数据开始,比较相邻的两个数据,(以升序为例)如果大就交换,得到一个最大数据在末尾;然后进行第二趟,只扫描前n-1个元素,得到次大的放在倒数第二位。以此类推,最后得到升序序列。如果在扫描过程中,发现没有交换,说明已经排好序列,直接终止扫描。所以最多进行n-1趟扫描。
② 时间复杂度:T(n) = O(n²)。
③ 空间复杂度:S(n) = O(1)。
④ 稳定性:稳定排序。
⑤ 程序:
void BubbleSort(RecordType r[], int length) { n = length; change = TRUE; for(i = 1; i <= n – 1 && change; i++) { change = FALSE; for(j = 1; j <= n – I; ++j) if(r[j].key > r[j + 1].key) { x = r[j]; r[j] = r[j + 1]; r[j + 1] = x; change = TRUE; } } }
2、 快速排序:
① 思想:冒泡排序一次只能消除一个逆序,为了能一次消除多个逆序,采用快速排序。以一个关键字为轴,从左从右依次与其进行对比,然后交换,第一趟结束后,可以把序列分为两个子序列,然后再分段进行快速排序,达到高效。
② 时间复杂度:平均T(n) = O(n㏒n),最坏O(n²)。
③ 空间复杂度:S(n) = O(㏒n)。
④ 稳定性:不稳定排序。{3, 2, 2}
⑤ 程序:
void QKSort(RecordType r[], int low, int high) { int pos; if(low < high) { pos = QKPass(r, low, high); QKSort(r, low, pos - 1); QKSort(r, pos + 1, high); } } int QKPass(RecordType r[], int left, int right) { RecordType x; int low, high; x = r[left]; low = left; high = right; while(low < high) { while(low < high && r[high].key >= x.key) high--; if(low < high) { r[low] = r[high]; low++; } while(low < high && r[low].key < x.key) low++; if(low < high) { r[high] = r[low]; high--; } } r[low] = x; return low; }
四、选择类排序:
(一) 思想:每一趟在n – i + 1 ( i = 1,2, … , n - 1)个记录中选取关键字最小的记录作为有序序列中的第i个记录。
(二) 分类:
1、 简单选择排序:
① 思想:第一趟时,从第一个记录开始,通过n – 1次关键字的比较,从n个记录中选出关键字最小的记录,并和第一个记录进行交换。第二趟从第二个记录开始,选择最小的和第二个记录交换。以此类推,直至全部排序完毕。
② 时间复杂度:T(n) = O(n²)。
③ 空间复杂度:S(n) = O(1)。
④ 稳定性:不稳定排序,{3, 3, 2}。
⑤ 程序:
void SelectSort(RecordType r[], int length) { n = length; for(i = 1; i <= n - 1; i++) { k = i; for(j = i + 1; j <= n; i++) if(r[j].key < r[k],key) k = j; if(k != i) { x = r[i]; r[i] = r[k]; r[k] = x; } } }
2、 树形选择排序:
① 思想:为了减少比较次数,两两进行比较,得出的较小的值再两两比较,直至得出最小的输出,然后在原来位置上置为∞,再进行比较。直至所有都输出。
② 时间复杂度:T(n) = O(n㏒n)。
③ 空间复杂度:较简单选择排序,增加了n-1个额外的存储空间存放中间比较结果,就是树形结构的所有根节点。S(n) = O(n)。
④ 稳定性:稳定排序。
⑤ 程序:
3、 堆排序:
① 思想:把待排序记录的关键字存放在数组r[1…n]中,将r看成是一刻完全二叉树的顺序表示,每个节点表示一个记录,第一个记录r[1]作为二叉树的根,一下个记录r[2…n]依次逐层从左到右顺序排列,任意节点r[i]的左孩子是r[2i],右孩子是r[2i+1],双亲是r[i/2向下取整]。然后对这棵完全二叉树进行调整建堆。
② 时间复杂度:T(n) = O(n㏒n)。
③ 空间复杂度:S(n) = O(1)。
④ 稳定性:不稳定排序。{5, 5, 3}
(1) 调整堆:
void sift(RecordType r[], int k, int m) { /*假设r[k...m]是以r[k]为根的完全二叉树,而且分别以r[2k]和r[2k+1]为根的左右子树为大根堆,调整r[k],使整个序列r[k...m]满足堆的性质*/ t = r[k];/*暂存“根”记录r[k]*/ x = r[k].key; i = k; j = 2 * i; finished = FALSE; while(j <= m && !finished) { if(j < m && r[j].key < r[j + 1].key) j = j + 1;/*若存在右子树,且右子树根的关键字大,则沿右分支“筛选”*/ if(x >= r[j].key) finished = TRUE;/*筛选完毕*/ else { r[i] = r[j]; i = j; j = 2 * i; }/*继续筛选*/ } r[i] = t;/*将r[k]填入到恰当的位置*/ }
(2) 建初堆:
void crt_heap(recordType r[], int length) { n = length; for(i = n / 2; i >= 1; --i)/*自第n/2向下取整 个记录开始进行筛选建堆*/ sift(r, i, n); }
(3) 堆排序:
void HeapSort(RecordType r[], int length) { crt_heap(r, length); n = length; for(i = n; i >= 2; --i) { b = r[1];/*将堆顶记录和堆中的最后一个记录互换*/ r[1] = r[i]; r[i] = b; sift(r, 1, i - 1);/*进行调整,使r[1…i-1]变成堆*/ } }
五、归并排序:
(一) 思想:
(二) 分类:
1、 归并排序:
① 思想:假设初始序列右n个记录,首先将这n个记录看成n个有序的子序列,每个子序列的长度为1,然后两两归并,得到n/2向上取整 个长度为2(n为奇数时,最后一个序列的长度为1)的有序子序列。在此基础上,在对长度为2的有序子序列进行两两归并,得到若干个长度为4的有序子序列。如此重复,直至得到一个长度为n的有序序列为止。
② 时间复杂度:T(n) = O(n㏒n)。
③ 空间复杂度:S(n) = O(n)。
④ 稳定性:稳定排序。
⑤ 程序:
void Merge(RecordType r1[], int low, int mid, int high, RecordType r2[]) { /*已知r1[low...mid]和r1[mid + 1...high]分别按关键字有序排列,将它们合并成一个有序序列,存放在r2[low...high]*/ i = low; j = mid + 1; k = low; while((i <= mid) && (j <= high)) { if(r1[i].key <= r1[j].key) { r2[k] = r1[i]; ++i; } else { r2[k] = r1[j]; ++j; } ++k; } while(i <= mid) { r2[k] = r1[i]; k++; i++; } while(j <= high) { r2[k] = r1[j]; k++; j++; } } void MSort(RecordType r1[], int low, int high, RecordType r3[]) { /*r1[low...high]经过排序后放在r3[low...high]中,r2[low...high]为辅助空间*/ RecordType r2[N]; if(low == high) r3[low] = r1[low]; else { mid = (low + high) / 2; MSort(r1, low, mid, r2); MSort(r1, mid + 1, high, r2); Merge(r2, low, mid, high, r3); } } void MergeSort(RecordType r[], int n) { /*对记录数组r[1...n]做归并排序*/ MSort(r, 1, n, r); }
六、分配类排序:
(一) 思想:分配类排序是利用分配和收集两种基本操作。
(二) 分类:
1、 多关键字排序:
2、 链式基数排序:
① 思想:先分配,再收集,就是先按照一个次关键字收集一下,然后进行收集(第一个排序),然后再换一个关键字把新序列分配一下,然后再收集起来,又完成一次排序,这样所有关键字分配收集完后,就完成了排序。
② 时间复杂度:T(n) = O( d ( n + rd ) )。
③ 空间复杂度:S(n) = O(rd)。
④ 稳定性:稳定排序。
七、总结:
(1)简单排序法一般只用于n较小的情况(例如n<30)。当序列的记录“基本有序”时,直接插入排序是最佳的排序方法。如果记录中的数据较多,则应采用移动次数较少的简单选择排序法。
(2)快速排序、堆排序和归并排序的平均时间复杂度均为O(n㏒n),但实验结果表明,就平均时间性能而言,快速排序是所有排序方法中最好的。遗憾的是,快速排序在最坏情况下的时间性能为O(n²)。堆排序和归并排序的最坏时间复杂度仍为O(n㏒n),当n较大时,归并排序的时间性能优于堆排序,但它所需的辅助空间最多。
(3)可以将简单排序法与性能较好的排序方法结合使用。例如,在快速排序中,当划分子区间的长度小于某值时,可以转而调用直接插入排序法;或者先将待排序序列划分成若干子序列,分别进行直接插入排序,然后再利用归并排序法,将有序子序列合并成一个完整的有序序列。
(4)基数排序的时间复杂度可以写成O(d·n)。因此,它最适合于n值很大而关键字的位数d较小的序列。当d远小于n时,其时间复杂度接近O(n)。
(5)从排序的稳定性上来看,在所有简单排序法中,简单选择排序是不稳定的,其他各种简单排序法都是稳定的。然而,在那些时间性能较好的排序方法中,希尔排序、快速排序、堆排序都是不稳定的,只有归并排序、基数排序是稳定的。
- 【模板】常用排序
- 常用排序模板
- 【模板】排序
- 排序模板
- 模板----排序
- 排序模板
- 常用模板
- 【排序】冒泡排序模板
- 【排序】选择排序模板
- 【排序】快速排序模板
- 【排序】插入排序模板
- 【排序】希尔排序模板
- 【排序】快速排序模板
- 最完整的 9 种常用排序算法 c++ 模板实现
- 常用排序
- 常用排序
- 常用排序
- 常用排序
- 关于NIO笔记(二):缓冲区Buffer
- RBAC打造通用web管理权限02
- Longest Common Prefix
- SSH学习之Struts2的环境搭建
- Javascript原型链
- 【模板】常用排序
- c语言程序设计现代方法---第四章:表达式
- git忽略文件和文件夹
- Java 类集框架
- Javascript闭包
- php中的命名空间
- CPU、内存、硬盘、指令
- poj1113(凸包)
- C++11新特性——range for