假设含有n个记录的序列为{R1,R2,R3,......,Rn},其相应的关键字序列为{K1,K2,K3,......,kn}。将这些记录重新排序为{Ri1,Ri2,Ri3,.......,Rin},
使得相应的关键字值满足条件Ki1 <= Ki2 <= Ki3 <= .... <= Kin,这样的一种操作称之为排序。
对于一个排序算法来说,一般从如下三个方面来衡量算法的优劣。
√。时间复杂度:主要是分析关键字的比较次数和记录的移动次数。
√。空间复杂度:分析排序算法中需要的辅助内存。
√。稳定性:若两个记录A和B的关键字值相等,但排序后A、B的先后次序保持不变,则称这种排序算法是稳定的;反之就是不稳定的。
就现有的排序算法来看,排序大致可分为外部排序和内部排序。如果整个排序过程不需要借助于外部存储器,所有排序操作都在内存中运行,这种排序可称之为内部排序。
如果参与的元素非常多,数据非常的大,计算机无法把整个排序过程放在内存中完成,必须借助于外部存储器,那么这种排序称之为外部排序。
外部排序基本思路:
1.把要排序的文件中的一组记录(将源文件分解成多个可以一次性装入内存)读入内存的排序区,对读入内存的记录按照内部排序法进行排序,排序之后输出带外部存储器。不断重复这一过程,每次读取一条记录。知道源文件所有的记录处理完毕。</span>
2.将上一步排序好的记录两组两组地合并排序,在内存容量允许的情况下,每组中包含的记录越大越好,这样可以减少合并的次数。
当然了我们常说的排序都是指内部排序。对于外部排序而言,(个人认都是公司或企业内部的机密,不到核心高层没必要去想那么多)。
直接选择排序的思想很简单。他需要经过n-1趟比较
第一趟比较:程序将记录定位在第一个数据上,拿第一个数据依次和它后面的每个数据进行比较,如果第一个数据大于后面某个数据,就交换他们。。。以此类推。经过第一趟比较,这组数据中最小的数据被选出,它排在第一位。</span>
第二趟比较:程序将记录定位在第二个数据上,拿第二个数据依次和它后面的每个数据进行比较,如果第二个数据大于后面某个数据,就交换他们。。。依次类推。经过第二趟比较,这组数据中第二小的数据被选出,它排在第二位。</span>
。。。
按此规则一共进行n-1趟比较,这组数据中第n-1小的数据被选出,被排在第n-1位;剩下的就是做大的数据,它排在最后。
假设有下面一组数据:
21, 30 ,49, 16, 9 ,30*
模拟效果如下:
//定义一个数据包装类
class DataWrap implements Comparable<DataWrap>{
int data;
String flag;
public DataWrap(int data, String flag){
this.data = data;
this.flag = flag;
}
@Override
public String toString() {
return data + flag;
}
public int compareTo(DataWrap dw) {
return this.data > dw.data ? 1 : (this.data == dw.data ? 0 : -1);
}
}
<pre name="code" class="java">public class SelectSort{
public static void selectSort(DataWrap[] data){
System.out.println( "-开始排序-");
int arrayLength = data.length;
//依次进行n-1趟比较,第i趟比较将第i大的值选出放在i位置上
for(int i = 0; i < arrayLength - 1; i ++){
//第i个数据只需和它后面的数据进行比较
for(int j = i + 1; j < arrayLength; j ++){
if(data[i].data > data[j].data){
DataWrap dw = data[i];
data[i] = data[j];
data[j] = dw;
}
}
//没进行一趟比较后输出结果
System.out.println( java.util.Arrays.toString(data));
}
}
}
public static void main(String[] args){
//21,30,49,30*,16,9,
DataWrap[] data = {new DataWrap(21,""),
new DataWrap(30,""),
new DataWrap(49,""),
new DataWrap(30,"*"), //为了查看稳定性如何
new DataWrap(16,""),
new DataWrap(9,"")};
System.out.println("-排序前-:"+java.util.Arrays.toString(data));
selectSort(data);
System.out.println("-排序后-:"+java.util.Arrays.toString(data));
}
}
输出结果为:
从上面的直接选择排序算法可以看出,直接选择排序算法的关键就是n-1趟比较,每趟的目的就是选择本趟比较的最小数据,并将它放在本趟比较的第一位。对于上面的实现算法,其实有一个很大的问题:就是每趟比较过程中,程序一旦发现某个数据比第一个数据小,就会立即交换他们。但这没有太大的必要,反而是增加了交换的次数,导致算法效率降低。
对于上面的算法进行了改进:
public class SelectSort{
public static void selectSort(DataWrap[] data){
/*System.out.println( "-开始排序-");
int arrayLength = data.length;
//依次进行n-1趟比较,第i趟比较将第i大的值选出放在i位置上
for(int i = 0; i < arrayLength - 1; i ++){
//第i个数据只需和它后面的数据进行比较
for(int j = i + 1; j < arrayLength; j ++){
if(data[i].data > data[j].data){
DataWrap dw = data[i];
data[i] = data[j];
data[j] = dw;
}
}
//没进行一趟比较后输出结果
System.out.println( java.util.Arrays.toString(data));
}*/
System.out.println( "-开始排序-");
int arrayLength = data.length;
for(int i = 0; i < arrayLength; i ++){
//minIndex 永远保留本趟比较中最小值的索引
int minIndex = i;
for(int j = i + 1; j < arrayLength; j ++){
if(data[minIndex].compareTo(data[j]) > 0){
<span style="white-space:pre"> </span>//将j的值赋给minIndex
minIndex = j;
}
}
<span style="white-space:pre"> </span>//每趟比较最多交换一次
if(minIndex != i){
DataWrap dw = data[i];
data[i] = data[minIndex];
data[minIndex] = dw;
}
System.out.println( java.util.Arrays.toString(data));
}
}
输出结果为:
从输出结果来看,直接选择排序的第n趟比较之多交换一次,且永远是那n-1位的数据和中间最小的一个数据进行交换(当然除了他本身就是最小的数据)。
对于注解选择排序算法而言,假设有n个数据,数据交换的次数最多有n-1次,但程序的比较次数较多。总体来说,其时间效率为 O(n^2)。
直接选择排序算法的空间效率很高,他只需要一个附加单元用于交换,其空间效率为 O(1)。
从上面程序中的两个为30的DataWrap的排序结果来看,直接选择排序是不稳定的。
参考资料见:《疯狂Java程序员的基本修养》 --李刚