各种排序算法:交换(冒泡,快速),
选择(直接选择,堆排序),
插入(直接插入,二分插入,希尔排序),
归并排序
1 冒泡排序
2 快速排序 (两种方法)
3 直接选择排序
4 堆排序
5 直接插入排序
希尔排序
二分法排序
6 归并排序
7 分配排序(基数排序,箱排序,计数排序) 没有实现
测试代码
各种算法的复杂度的一个总结
参考:排序算法实现及分析
=================================================================================
冒泡排序:就像气泡一样,当前元素和下一个比,合适就这样,不合适就交换折腾 n * n次
//1 双重循环 ----里面的范围:[0,n-1-i] 比较的是j和j+1 2 每一次都要交换 3 优化代码,加上交换过标志 flag(若flag=false表示排好序); void sort_bubble( int arr[],int num ) { bool flag=true; for(int i=0;i<num-1&&flag;i++) //第 i趟循环: 总共是 n-1趟循环 { flag=false; //遇到 小(大)的数值往后面浮动,先把后面的i个已经排好序了:[0,num-i] [i,num](已经拍好了) for(int j=0;j<num-i;j++) { if(arr[j]<arr[j+1]) { swap_element(arr+j,arr+j+1); flag=true; } } /*printArray(arr,num);*/ } printArray(arr,num); }
快速:元素找到自己的排序位置,当每个人都找到了,那个顺序就定了。
//快速排序(冒泡法的改进:交换排序) 第一次调用 sort_Quick(arr,0,len)--- //一定要把pos隔离出来 //做一趟 快速排序:arr[left] 的位置。 int find_pos( int * arr, int left, int right ) { int low=left,high=right; int tmp=arr[left]; //要找的 数值就保存在这里了,腾空一个位置。 while(low<high) //结束的时候 low==high,也就是需要返回的 值所在的位置。 { while(low<high&&arr[high]>tmp) high--; arr[low]=arr[high]; //把比较小的移动到 low端(或者直接调换顺序开个头) while(low<high&&arr[low]<tmp) low++; arr[high]=arr[low]; //把比较大的移动到high端 } arr[low]=tmp; return low; } int find_pos2(int * arr, int left, int right) { int cmpValue = arr[right]; int theIndex = left; for(int i = left; i < right; ++i) { if(arr[i] < cmpValue) { swap_element(arr+i,arr+theIndex); theIndex++; } } swap_element(arr+theIndex,arr+right); //arr+theIndex是 大于等于arr+right的 return theIndex; } void sort_quick( int arr[],int low,int high ) { int pos; if(low<high) //low==high的时候结束递归。 { /*pos=find_pos(arr,low,high);*/ pos=find_pos2(arr,low,high); sort_quick(arr,low,pos-1); //左边的部分 sort_quick(arr,pos+1,high); //右边的部分 } }
选择:老实的排序法,找到最值,和当前的位置进行交换。
//1 每次取出来 最小的([i+1,n]) 2 和当前的i进行交换(判断j和i是否相等) void sort_select( int arr[],int num ) { int min; //记录下 每一趟排序的 最小的那个下标。 for(int i=0;i<num-1;i++) //i 表示轮到了 哪一个下标的元素 { min=i; for(int j=i+1;j<num;j++) { if(arr[j]<arr[min]) { min=j; } } //找到这一段的最小的值之后,与 arr[i]交换位置。 if(i!=min) { swap_element(arr+i,arr+min); } printArray(arr,num); } }
堆:和选择一样建一个具有堆的性质二叉树(节点永远比子节点大),堆顶就是最值,拿出来,再建一次堆(i-1个)。
//从大到小(或者从小到大)排列:主要考虑的是 p(i) 以及 p.left(2*i-1)或者p.right(2*i)的较小或者较大值----然后交换或者退出 void siftdown( int arr[], int nodeN, int maxN) { int tmp=arr[nodeN]; int child=nodeN*2+1,parent=nodeN; while(child<=maxN) { if(child<maxN&&arr[child+1]<arr[child]) //存在右子树序列 { child=child+1; //得到最小的结点。 } if(tmp<=arr[child]) break; else { arr[parent] = arr[child]; parent = child; //往 下面的层数进行扩展 child = child*2+1; } } arr[parent]=tmp; } // 1 初建堆---符合堆的概念 [n....2],其中 根结点 arr[0]最小 //2 反复调整排序 [n....2]: 选择交换(arr[0],arr[n]),选取最(大)小的(arr[0]). void sort_stack(int arr[],int num ) { for(int i=(num-1)/2;i>=0;i--) { siftdown(arr,i,num-1); } cout<<"最小的元素是:"<<arr[0]<<endl; for(int n=num-1;n>=1;n--) { swap_element(arr,arr+n); siftdown(arr,0,n-1); printArray(arr,num); } printArray(arr,num); }
5 插入排序
插入:随便拿一个向有序的中放。
开始只有找一个元素参照,一个必然是有序的,然后可以结合二分法查找,来排序,用查找的思想排序,逆天了
// 排列好的[1,i-1] 没有排列好的部分:[i,n]:只是 找到i这个位置和[1,i-1]比较和交换。 // 1 比较 2 移动(往后面移动),腾出位置来 3 插入到最后的一个位置。 void sort_insert( int arr[],int num ) { int curValue; int j; for(int i=0+1;i<num;i++) { curValue=arr[i]; j=i-1; while(arr[j]>curValue&&j>=0) { arr[j+1]=arr[j]; j--; } arr[j+1]=curValue; printArray(arr,num); } } void sort_Binaryinsert( int arr[],int num ) { int low,high,mid; int curValue; for(int i=0+1;i<num;i++) { curValue=arr[i]; low=0; high=i-1; while(low<=high) //结束条件:low=high+1(即为需要插入的位置); 1 2 4 5 6 3 { mid=(low+high)/2; if(arr[mid]<curValue) { low=mid+1; } else { high=mid-1; } } //2 往后面移动和插入数值 for(int j=i-1;j>=high+1;j--) { arr[j+1]=arr[j]; } arr[high+1]=curValue; printArray(arr,num); } } //其实和 直接插入排序 大致相同,只是间隔gap换成 1就可以了 void sort_shellComplete( int arr[], int num, int gap ) { int curValue; int j; for(int i=0+gap;i<num;i++) { curValue=arr[i]; /*j=i-gap; while(arr[j]>curValue&&j>=0) { arr[j+gap]=arr[j]; j-=gap; }*/ for(j=i-gap;j>=0&&arr[j]>curValue;j=j-gap) { arr[j+gap]=arr[j]; } arr[j+gap]=curValue; printArray(arr,num); } } void sort_shell( int arr[],int num ) { for(int i=num/2;i>=1;i=i/2) { sort_shellComplete(arr,num,i); } }
6 归并:几组有序的合并成一个。很简单,每人轮流拿出一个比较下,放进篮子里不就完了。
void merges( int arr[], int low, int mid, int high ) { //前半部分:[low,mid] 后半部分:[mid+1,high] 然后分别比较→赋值(再移动即 循环) int i=low,j=mid+1,k=low; //i,j两个坐标的变化;k--新的组成的数组的下标的变化-- k=0错误 int tmparr[50]; while(i<=mid&&j<=high) //结束条件就是:其中一个条件不满足(其中一个已经用完了) { if(arr[i]<arr[j]) { tmparr[k++]=arr[i++]; } else { tmparr[k++]=arr[j++]; } } //好好处理一下的部分 while(i<=mid) { tmparr[k++]=arr[i++]; } while(j<=high) { tmparr[k++]=arr[j++]; } //合并好的 数组元素付给 需要合并的数组。 for(int i=low;i<=high;i++) { arr[i]=tmparr[i]; } } void sort_ReMerge( int arr[], int low, int high ) { int mid; if(low<high) //最后: 单个的元素(low==high) 结束递归。 { mid=(low+high)/2; sort_ReMerge(arr,low,mid); //前半段分 sort_ReMerge(arr,mid+1,high); //后半段分 merges(arr,low,mid,high); //前后 两段合并 } } void sort_merge( int arr[],int num) { sort_ReMerge(arr,0,num-1); printArray(arr,num); }
测试代码
void printArray(int arr[],int num) { cout<<"排序后的数组元素是:"<<endl; for(int i=0;i<num;i++) { cout<<arr[i]<<endl; } }
void swap_element(int *a,int *b) { int tmp; tmp=*a; *a=*b; *b=tmp; }
int main() { int arr[]={101,87,25,90,54,21,99,27,76}; int num=sizeof(arr)/sizeof(int); printArray(arr,num); //sort_bubble(arr,num); //sort_quick(arr,0,num-1); //sort_select(arr,num); //sort_insert(arr,num); //sort_shell(arr,num); //sort_Binaryinsert(arr,num); //sort_stack(arr,num); //sort_merge(arr,num); return 0; }
====================================================================================
各种算法的复杂度的一个总结
直接选择排序、快速排序、希尔排序、堆排序不是稳定的排序算法,
而冒泡排序、插入排序、归并排序和基数排序是稳定的排序算法。
最好 最坏 平均 稳定 辅助空间
快速排序 O(nlogn) O(n^2) O(nlogn) 否 不要
冒泡排序 O(n) O(n^2) O(n^2) 是 不要
直接选择排序 O(n^2) O(n^2)O(n^2) 否 不要
堆排序 O(nlogn) O(nlogn) O(nlogn) 否 不要
直接插入排序 O(n) O(n^2) O(n^2) 是 不要
希尔排序 O(n) O(n^2) O(n^2) 否 不要
归并排序 O(nlogn) O(nlogn) O(nlogn) 是 要(1.5?)