我们关注的对象是重新排列数组元素的算法,其中每个元素都有个主键,当然这个主键并不是数据库的primary key,数据库的主键是唯一的,而我们元素的主键是按照某种方式排列,例如数字大小(1,2,3....)又例如字母排序(a,b,c.....).元素通常是对象,而主键通常是通过一种内置的机制 。(comparable接口),一般来说,索引较大元素的主键大于索引较小元素的主键。
因为结果相同,只是实现机制不一样,所以我们有必要将这种排序算法模块化,这个模块我将用java重新定义排序类及其类中方法的含义。
Sort():排序代码放入sort()方法中
Less()与exch():f辅助函数
因为还有不同的算法,所以我们为相同的类取不同的名字。
下面我给出一般模板
public class Example
public static void sort(Comparable[] a){
//算法实现代码
}
public static boolean less(Comparable v,Comparable w){
//v是否小于w ,小于w返回-1,-1<0,返回true
return v.compareTo(w)<0;
}
public static void exch(Comparable[] a,int i,int j){
//交换元素
Comparable t=a[i];a[i]=a[j];a[j]=t;
}
public static void show(Comparable[] a){
//打印数组元素
for (int i = 0; i < a.length; i++) {
System.out.println(a[i]+"");
}
}
public static boolean isSorted(Comparable[] a){
//测试数组元素是否有序
for (int i = 0; i < a.length; i++) {
if(less(a[i],a[i-1]))
return false;
}
return true;
}
}
另外,我们在测试这个模板的时候,要考虑无论数组的初始状态是什么,我们都能成功吗,谨慎起见,我们还会加一句assert isSorted(a),来确认排序后的元素都是有序的。
此外,还要评估每种算法的运行时间,也就是算法的性能。
在研究排序算法时,我们需要计算比较和交换的数量,对于不交换元素的算法,要考虑访问数组的次数。额外的内存使用和运行时间同等重要,排序算法可以分为两类:除了函数调用所需的栈和固定数目的实例变量之外无需额外内存的原地排序算法(这个就是exch函数,请读者细细体会),以及需要额外内存空间来存储另一份数组副本的其他排序算法。
数据类型
我们的数据类型适用于任何实现了Comparable接口的数据类型,所以说数字类型的Integer和Double以及String还有其他数据类型,如File和URL都能调用我们的排序算法,因为它们都实现了Comparable接口,简直是爽歪歪。
需要注意的是Comparable接口的CompareTo方法不一定会用到实例所有的实例变量,毕竟元素的主键很可能是每个元素的一小部分,另外CompareTo必须实现一个全序关系--即自反性,反对称性以及传递性。
自反性:对所有的v,v=v
反对称性:对于所有的v<w都有v>w 且v=w时w=v
传递性:对于所有的v,w,x 如果v<=w且w<=x 则v<=x
重点:Comparable接口是对元素主键的抽象
CompareTo方法是对元素主键的实现
选择排序
了解了这些,就开始研究具体的算法了,首先我们先研究最简单的算法——选择排序。
步骤:首先找到数组中最小的元素,将它与数组第一个元素交换,那么数组的第一个元素就成了最小的元素,然后再从剩下的元素中(除第一个以外的元素)找到最小的元素,将它与第二个元素交换,那么数组中第二个元素成了本数组第二小的数组,如此往复,直到将数组中的所有元素排序。
先用程序实现
public class Selection {
public static Comparable[] sort(Comparable[] a){
int N=a.length;
for (int i = 0; i < N; i++) {
int min=i;
//剩下元素中寻找最小值,这一步只是记下最小值的坐标
//注意j=i+1的原因是我们将0赋值给min了,后边会比较a[0](也就是min)与a[1]的大小
for(int j=i+1;j<N;j++){
if(Example.less(a[j],a[min])){
min=j;//寻找最小元素,记下元素索引位置
}
Example.exch(a,i,min);//交换最小元素与数组第i个元素的位置
}
}
return a;
}
}
该算法将第i小的元素放到a[i]中 ,数组的第i个位置的左边是第i个最小元素且它们不会再被访问。
画图实现选择排序
蓝色圈起来的数字表示查找的最小值 红色圈起来的表示需要比较的元素。
如上图,可以推出
对于长度为N的数组 选择排序需要N^2/2次比较和N此交换
总的来说 选择排序有两个鲜明的特点
1. 运行时间与输入无关:为了找出最小的元素扫描一遍数组信息并不能为下一组提供额外信息,这个性质在某种情况下时缺点,例如 随机数组和有序数组在这种算法下排序所花费的时间居然一样长!
2. 数据移动是最少的,交换次数与数组长度是线性关系。