一、插入排序
基本思想:对于每一个待排数组,从第二个元素开始,跟它之前的元素比较,找到合适的位置,将对应元素后移后插入到该位置。
例如待排数组 A=[6,2,4,1,5,9]。
第一次循环:查找元素2应该在的位置,与前面元素(只有6)比较,所以将6移到2的位置,然后将2移到6的位置;
第二次循环:查找元素4应该在的位置,与前面元素(2、6)比较,小于6,因此将6往后移动,再与2比较,不需要移动,故插入到6所在的位置;
...
以此类推,知道最后一个元素找到合适的位置。
sort1:
for i = [1,n)
for ( j = i; j>0 && A[j-1]>A[j]; j--)
swap(A[j-1]. A[j])
结合学习笔记3性能篇可知,可以把代码改成如下形式:
sort2:
for i = [1,n)
for ( j = i; j>0&&A[j-1]>A[j]; j--)
t = A[j]; A[j] = A[j-1]; A[j-1] = t;
即将swap函数写成内联的形式,可以提高运行速度。
通过上面一步的改进又可以发现,不用每次交换都引入一个临时变量 t ,于是又可以得到下面这个更加优化的版本:
sort3:
for i = [1, n)
t = A[i];
for ( j = i ; j>0&&A[j-1]>A[j]; j--)
A[j] = A[j-1];
A[j-1] = t;
显然,从执行效率上来看,sort3 > sort2 > sort1。
二、快速排序
基本思想:基于分治法,排序数组时,将数组划分成为两个较小的部分,然后递归排序他们。
例如现有一个具有8个元素的数组:X = [55,44, 59,26,53,58,97,93]
首先以第一个元素55为中间元素,将数组划分为 [44 26 53 55 59 58 97 93]
然后分别递归子数组[44 26 53]和[59 58 97 93]
...
qsort1:
void qsort1(l, u)
bool go_on = true;
if l>=u
return; //只有一个元素,无需排序
t=x[l];
i = l+1;
j = u;
while(go_on)
while(i<=u && x[i]<t)
i++;
while(x[j]>t)
j--;
if(i>j)
go_on = false;
else
swap(x[i],x[j]);
swap(x[l],x[j]);
qsort1(l,j-1);
qsort1(j+1,u);
双向扫描,当然如果不想每次都以第一个元素为值,可以生成一个(l, u)之间的随机数作为r,以x[r]作为中间值。
书上还引入了一个中间变量cutoff,相应地在程序中将 返回条件 l>=u 改为 u-l>cutoff ,虽然程序结束时数组并不是有序的,但是被组合成一小块一小块随机顺序的值。
扩展:编写程序,让该程序在O(n)的时间内从数组x[0,1,...,n-1]中找出第k个最小的元素。
void selectk(l,u,k)
bool go_on = true;
if l>=u
return;
t = x[l]; i = l+1; j = u;
while(go_on)
while(i<=u && x[i]<t)
i++;
while(x[j]>t)
j--;
if(i>j)
go_on = false;
else
swap(x[i],x[j]);
swap(x[l],x[j]);
if( j > k )
selectk(l,j-1,k)
else if(j<k)
selectk(j+1,u,k)
此算法在快排的基础上添加判断得到。