说明:代码使用codeblocks编译,C++实现,个人编写,如有错误,还望指正。
总结
排序方法 | 稳定性 | 平均时间复杂度 | 最优时间复杂度 | 最坏时间复杂度 | 空间复杂度 |
---|---|---|---|---|---|
插入排序 | 稳定 | O( n 2 n^{2} n2) | O( n n n) | O( n 2 n^{2} n2) | 1 |
冒泡排序 | 稳定 | O( n 2 n^{2} n2) | O( n 2 n^{2} n2) | O( n 2 n^{2} n2) | 1 |
二路归并排序 | 稳定 | O( n l o g nlog nlog) | |||
二叉树排序 | 稳定 | O( n l o g n nlogn nlogn) | |||
计数排序 | 稳定 | O( n + k n+k n+k) | |||
桶排序 | 不稳定 | O(n) | |||
选择排序 | 不稳定 | O( n 2 n^{2} n2) | O( n 2 n^{2} n2) | O( n 2 n^{2} n2) | 1 |
堆排序 | 不稳定 | O( n l o g n nlogn nlogn) | O( n l o g n nlogn nlogn) | O( n l o g n nlogn nlogn) | 1 |
希尔排序 | 不稳定 | O( n l o g n nlogn nlogn) | |||
基数排序 | 不稳定 | O( n + k n+k n+k) | |||
快速排序 | 不稳定 | O( n l o g n nlogn nlogn) | O( n l o g n nlogn nlogn) | O( n 2 n^{2} n2) | O( n l o g n nlogn nlogn) |
插入排序
插入排序把序列分为两个部分,一部分有序,一部分无序。每一次循环都把无序的第一个元素插入到有序的序列中。
以序列 {4,2,1,3,5}为例。
当一个序列中只有一个元素的时候,我们认为这个序列有序。所以最开始的时候,有序和无序序列如下:
{4|, 2,1,3,5}
接下来取无序中的第一个元素,和有序元素中的最后一个元素,也就是最大的元素比较,如果大于,则不需要移动,去下一个无序元素即可,如果小于,则一直交换到合适的位置。
取2和4比较,小于,则和4交换位置{2,4|,1,3,5},发现到达有序的第一个位置,则停止。
然后取1,和4比较,小于则交换{2,1,4|,3,5},继续和2比较交换{1,2,4|,3,5}至此1的位置也找到了。
接下来元素3,需要和4比较交换{1,2,3,4|,5},继续和2比较不交换,结束。
元素5 和4比较不交换,结束。
整个过程可以在原序列上直接操作,只需要一个临时的变量用于交换。所以空间复杂度认为是O(1);
时间上,如果本来是有序的队列,每个元素只需要和自己前一位的元素比较一次就可以完成排序,这个是最优时间复杂度:O(n),相反,如果这个一个倒序的序列,第n元素都需要和n-1个元素比较,这个时候是最坏时间复杂度:O(n2)。平均时间复杂度是O(n2);
插入排序是稳定的。
void insert_sort(int array[],int len)
{
int v=0;
int j=0;
for(int i=1;i<len;i++)
{
v=array[i];
for(j=i-1;j>=0;j--)
{
if(array[j]>v)
array[j+1]=array[j];
else
break;
}
array[j+1]=v;
}
}
冒泡排序
冒泡排序总是比较和交换相邻的两个元素。再一次循环中,获得到一个最大的值,把他交换到序列的最后,然后再剩下的序列中同样找到一个最大的数。
以序列 {4,2,1,3,5}为例。
寻找最大的数字,比较交换,放到最后:4和2比较交换。
{2, 4,1,3,5}
4和1比较交换:{2, 1,4,3,5}
4和3比较交换:{2, 1,3,4,5}
4和5比较不交换{2, 1,3,4,5},这一轮循环下来,得到了最大的值5:{2, 1,3,4,|5}
第二轮以同样的方式:2和1比较交换,2和3比较不交换,3和4比较不交换,得到{1, 2,3,|4,5}
在经过两轮的比较就完成了排序。
整个过程可以在原序列上直接操作,只需要一个临时的变量用于交换。所以空间复杂度认为是O(1);
时间上,不管序列是否有序,我们都需要进行比较,一共需要比较(n-1)轮,每轮比较的次数是n的线性表达式。我们认为最优。最坏。平均的时间复杂度都是O(n2)。
冒泡排序是稳定的。
void insert_sort(int array[],int len)
{
int v=0;
int j=0;
for(int i=1;i<len;i++)
{
v=array[i];
for(j=i-1;j>=0;j--)
{
if(array[j]>v)
array[j+1]=array[j];
else
break;
}
array[j+1]=v;
}
}
选择排序
void insert_sort(int array[],int len)
{
int v=0;
int j=0;
for(int i=1;i<len;i++)
{
v=array[i];
for(j=i-1;j>=0;j--)
{
if(array[j]>v)
array[j+1]=array[j];
else
break;
}
array[j+1]=v;
}
}
快速排序
快速排序采用分治法,把一个串,按照一个基准值分为左右两部分,将以同样的方式排序左右两部分,每一次调用的结果固定了基准值的最终位置。:在下面的代码中,基准值选择串尾的数,基准值左面都比它小,右面都比它大
int Partition(int array[],int len,int start,int end)
{
if(array== NULL || len<=0 || start<0 || end>=len)
return -1;
int val=array[end];
int devide=start;
int tmp;
for(int i=start;i<=end;i++)
{
if(array[i]<val)
{
tmp=array[devide];
array[devide]=array[i];
array[i]=tmp;
devide++;
}
}
tmp=array[devide];
array[devide]=array[end];
array[end]=tmp;
return devide;
}
void quick_sort(int array[],int len,int start,int end)
{
if(start==end)
return ;
int index=Partition(array,len,start,end);
if(index > start)
quick_sort(array,len,start,index-1);
if(index < end)
quick_sort(array,len,index+1,end);
}
堆排序
1、建最大堆。
2、把堆顶元素放在末尾。
3、把这个堆的最后一个元素删除,重新调整这个堆。
4、重复2、3;
void max_heap_adjust(int array[],int len,int i)
{
int tmp=0;
int left=2*i+1;
int right=2*i+2;
int m=0;
if(left<=len)
{
m=left;
}
if(right<=len&&array[right]>array[m])
{
m=right;
}
if(m>i&&array[m]>array[i])
{
tmp=array[m];
array[m]=array[i];
array[i]=tmp;
max_heap_adjust(array,len,m);
}
}
void build_maxheap(int array[],int len)
{
//从最后一个有孩子的节点下标开始
int last_father=len/2;
for(int i=last_father;i>=0;i--)
{
max_heap_adjust(array,len,i);
}
return;
}
void heap_sort(int array[],int len)
{
if(array==NULL || len<=0)
return ;
build_maxheap(array,len-1);
int tmp;
for(int i=len-1;i>0;i--)
{
tmp=array[0];
array[0]=array[i];
array[i]=tmp;
max_heap_adjust(array,i-1,0);
}
return ;
}