只要是接触编程,接触算法,排序是必修的一门基础课,它的应用之大不言而喻,下面就几种常用排序进行实现并进行时间复杂度的分析:
1:选择排序法
举个例子:对于2 9 5 4 8 1 6
step1:在列表中先选择最大元9并与最后一个数字6交换 2 6 5 4 8 1 9;
step2:在剩余列表选择最大元8并与最后一个数字1交换 2 6 5 4 1 8 9;
step3:在剩下列表选择最大元6并与最后一个数字1交换 2 1 5 4 6 8 9;
step4:在剩下列表选择最大元5并与最后一个数字4交换 2 1 4 5 6 8 9;
step5:剩下列表最大元4已经在合适位置不用交换;
step6:在剩下列表选择最大元2并与最后一个数字1交换 1 2 4 5 6 8 9;
最后只剩一个数字,无需再排序,排序完成。
算法时间复杂度分析:第一步迭代需要n-1次比较操作;第二步需要n-2个比较。。。C表示其他常见操作(赋值,每次迭代的比较)
T(n)=n-1+n-2+...+1+c*(n-1)=n^2/2-n/2+c*(n-1)=O(n^2)
代码:
void sort_sele(int a[],int size){ int i,j; int k,max; for(i=0;i<size-1;i++) { k=0; max=a[0]; for(j=0;j<size-i;j++) if(max<a[j]) {max=a[j];k=j;} if(k!=i){int t=a[k]; a[k]=a[size-1-i]; a[size-1-i]=t;} }}2:插入排序法
还是例子2 9 5 4 8 1 6
step1:初始时,已排序列表只含列表第一个元素。将9插入:2 9;
step2:已经排序的2,9,将接下来的5插入列表:2 5 9;
step3:已经排序的2 5 9 ,将接下来的4插入列表:2 4 5 9;
step4:已经排序的2 4 5 6,将接下来的8插入列表:2 4 5 8 9;
step5:已经排序的2 4 5 8 9,将接下来的1 插入列表:1 2 4 5 8 9;
step6:已经排序的1 2 4 5 8 9,接下来的6插入列表:1 2 4 5 6 8 9.
step:整个排序完成。
算法性能:第k个迭代,需要将元素插入一个规模k的有序数组,需要k次比较k次数据移动,C是其他操作
T(n)=2*(1+2+...+n-1)+c*(n-2)=O(n^2 ;
代码:
void sort_inse(int a[],int size){ int i,j; int re; for(i=1;i<=size-1;i++){ j=i-1; re=a[i]; while(j>=0&&a[j]>re){a[j+1]=a[j];j--;} a[j+1]=re;} }3:冒泡排序算法
例子:2 9 5 4 8 1 6
step1:将2 9交换,因为已经是升序不用再动;将9 5交换成5 9;将9 4 交换成4 9;将9 8交换成8 9;将9 1交换成1 9;将9 6交换成6 9;----最大元素沉底2 5 4 8 1 6 9;
step2: 类似的将8沉底得到2 4 5 1 6 8 9;
step3:类似的将6沉底得到2 4 5 1 6 8 9;
step4:类似的将5沉底得到2 4 1 5 6 8 9;
step5:类似的将4沉底得到2 1 4 5 6 8 9;
step6:类似的将2沉底得到1 2 4 5 6 8 9;
最后完成
算法分析:
在最好情况时第一步就发现已经有序,则无需再操作,比较此时n-1,O(n);
最坏情况:总共n-1次扫描;第k次扫描需要比较n-k次比较
T(n)=1+2+...(n-1)+C*(n-1)=O(n^2);
代码:
void sort_mao(int a[],int size){ int i,j; int k,t; for(i=0;i<size-1;i++) for(j=0;j<size-1-i;j++){if(a[j]>a[j+1]) {t=a[j];a[j]=a[j+1];a[j+1]=t;} }}4:归并排序算法:
它是一种递归算法,将数组划分两半,对每一半不断递归知道得到一个元素的数组,然后对最终子数组进行归并排序
算法性能分析:T(n)=T(n/2)+T(n/2)+合并时间
第一个T(n/2)是数组前一半所需,第二个T(n/2)是数组后一半所需,合并时间包括n-1次比较和n次n次移动
T(n)=T(n/2)+T(n/2)+2*n-1=2*T(n/2)+2*n-1=2*(2*T(n/4)+2*n/2-1)+2*n-1=...=2nlogn+!=O(nlogn);
代码:
////归并算法之分半部分 void copy(int source[],int sindex,int dest[],int dindex,int len){ for(int i=0;i<len;i++) dest[i+dindex]=source[i+sindex];}//将一个数组sindec处开始的len个值赋给dest
//归并算法之归并部分 void merge(int a[],int len1,int b[],int len2,int temp[]){ int i1=0; int i2=0; int i3=0; while(i1<len1&&i2<len2){ if(a[i1]>b[i2]) temp[i3++]=b[i2++]; else temp[i3++]=a[i1++];}//将a,b里面较小的放入temp,直到一个放完 while(i1<len1) temp[i3++]=a[i1++]; while(i2<len2) temp[i3++]=b[i2++];//将还有剩余的数组放入 }
//归并算法 void mergesort(int a[],int size){ if(size>1){ int *half1=new int[size/2]; copy(a,0,half1,0,size/2); mergesort(half1,size/2); int *half2=new int[size-size/2]; copy(a,size/2,half2,0,size-size/2); mergesort(half2,size-size/2);//归并算法之分半部分 int *temp=new int[size]; merge(half1,size/2,half2,size-size/2,temp);//归并算法之归并部分 copy(temp,0,a,0,size); delete []half1; delete []half2; delete []temp;} }5:快速排序算法:
首先在数组中选择一个元素做枢轴,将数组分为两部分,第一部分都小于枢轴,第二部分都大于,随后对子数组递归使用快速排序
例如2 9 5 4 8 1 6
step1:选择第一个元素2为枢轴,分成两部分:1 2 5 4 8 9 6
step2:对于5 4 8 9 6 选择第一个元素5为枢轴,分成4 5 8 9 6
step3:对于8 9 6以排好序
算法性能:快速算法主要分为划分函数部分,在最坏情况下,需要花费n次比较,n次移动故划分时间复杂度O(n)
T(n)=T(n/2)*2+n(划分时间)=O(n*logn)
代码:
//快速排序之划分 int part(int a[],int first,int last){ int pivote=a[first]; int low=first+1; int high=last; while(high>low){ //从左向右搜索比枢轴大的 while(low<=high&&a[low]<=pivote)low++; //从右向左搜索比枢轴小的 while(low<=high&&a[high]>pivote) high--; //直到第一个大于枢轴的左边和第一个小于枢轴的右边出现,交换 if(high>low){int t=a[low];a[low]=a[high];a[high]=t;} } //返回此次枢轴的坐标 while(high>first&&pivote<=a[high])high--; if(pivote>a[high]){a[first]=a[high];a[high]=pivote; return high;} else return first;}
//快速排序 void quicksort(int a[],int first,int last){ if(last>first){int pri=part(a,first,last); quicksort(a,first,pri); quicksort(a,pri+1,last);} } void quicksort(int a[],int size){ quicksort(a,0,size-1);}5:堆排序算法
二叉树满足:每个节点都大于等于其任何子节点:首先使用heap类创建一个对象,使用add函数将元素入堆,然后使用remove函数删除返回,从而完成排序
算法性能分析:h表示一个n个元素的堆的高度,由于堆是完全二叉树,所以第K层有2^(k-1)个节点,第h层至少一个节点,至多2^(h-1)
1+2.。。+2^(h-2)<n<=1+2.。。+2^(h-2)+2^(h-1)--->可以得到 h-1<log(n+1)<=h,那么堆的高度log(n+1)<=h<log(n+1)+1,O(logn);由于add是从叶至根的,最多h个步骤添加一个元素那么对于一个n的数组,创建一个堆需要O(n*logn),remove函数是根到叶,删除根节点后重建堆的操作至多花费h个操作,同add一样调用了n次,所以O(n*logn)。
T(n)=O(n*logn)+O(n*logn)=O(n*logn)
代码:
#include"heap.h" void sort_sort(int a[],int size){ heap<int> heap; for(int i=0;i<size;i++) heap.add(a[i]); for(int i=0;i<size;i++) a[i]=heap.remove();}
二、选择排序、快速排序、希尔排序、堆排序不是稳定的排序算法,而 冒泡 排序、插入排序、归并排序和基数排序是稳定的排序算法。