目录
选择排序
选择排序简单介绍
先把最小的拿出来
剩下的,再把最小的拿出来
剩下的,再把最小的拿出来
......
每次选择还没处理的元素里最小的元素
我们每一次找剩下的元素中最小的元素,我们只需要把这最小的元素直接放在数组的开头就行了,也就是直接利用当前的数组的空间,就可以实现原地排序。
j从i出发,扫描后面所有的元素,找到其中最小的元素,将其命为minIndex,将其与第i个元素交换位置。
实现选择排序法
1.首先从原始数组中选择最小的1个数据,将其和位于第1个位置的数据交换。
2.接着从剩下的n-1个数据中选择次小的1个元素,将其和第2个位置的数据交换。
3.然后,这样不断重复,直到最后两个数据完成交换。至此,便完成了对原始数组的从小到大的排序。
不断从未排序的元素中选择最小的元素存放到排序序列的起始位置,然后再将剩余未排序元素中寻找最小元素存放到已排序序列的末尾。以此类推,直到所有元素均有序。
public class SelectionSort {
public SelectionSort() {
}
public static void sort(int[] arr){
//arr[0...i)是有序的; arr[i...n) 是无序的s
for (int i = 0; i < arr.length; i++) {
//选择arr[i...n)中的最小值的索引
int minIndex = i;
for (int j = i;j < arr.length;j++){
//在剩余的元素中找到最小的(比较查找)
if (arr[j]<arr[minIndex]){
minIndex = j;
}
}
//将arr[i]与arr[minIndex]交换位置
swap(arr,i,minIndex);
}
}
private static void swap(int[] arr, int i, int j) {
int t = arr[i];
arr[i] = arr[j];
arr[j] = t;
}
public static void main(String[] args) {
int[] arr = {1,4,2,3,6,5};
SelectionSort.sort(arr);
for (int item:arr){
System.out.print(item+" ");
}
}
}
当前只能实现int类型的数组进行排序,因此需要使用到泛型。
使用带约束的泛型
只需要在static后面加上<E>,就代表这个方法是泛型方法,他处理E这样的一个类型,这个类型具体由用户调用的时候来指定,相应的数组就可以指定为E类型。
public static <E> void sort(E[] arr)
但是e类型不一定可以用 < 来运算,所以我们需要对泛型E进行约束,使之这个泛型是可比较的(Comparable接口里面有一个泛型T,T的选择为可以与之比较的对象的类型,一般就是实现该接口类的本身,可以这样想和Person类比较的当然是Person本身了)。关于Comparable接口的介绍
public class SelectionSort {
public SelectionSort() {
}
//
public static <E extends Comparable<E>> void sort(E[] arr){
//arr[0...i)是有序的; arr[i...n) 是无序的s
for (int i = 0; i < arr.length; i++) {
//选择arr[i...n)中的最小值的索引
int minIndex = i;
for (int j = i;j < arr.length;j++){
//在剩余的元素中找到最小的(比较查找)
if (arr[j].compareTo(arr[minIndex]) < 0){
minIndex = j;
}
}
//将arr[i]与arr[minIndex]交换位置
swap(arr,i,minIndex);
}
}
private static <E> void swap(E[] arr, int i, int j) {
E t = arr[i];
arr[i] = arr[j];
arr[j] = t;
}
public static void main(String[] args) {
Integer[] arr = {1,4,2,3,6,5};
SelectionSort.sort(arr);
for (int item:arr){
System.out.print(item+" ");
}
}
}
此时方法已经修改成一个泛型方法,对于这个类型还有一个约束,其必须是可比较的,展现在JAVA语言当中就是实现comparable接口,很多排序算法都必须保证可比较。
使用 Comparable 接口
为了体现将其修改成一个泛型方法的优势,我们使用一个自定义的Student类来实现排序算法。
import java.util.Objects;
public class Student implements Comparable<Student>{
private String name;
private int score;
public Student(String name, int score) {
this.name = name;
this.score = score;
}
@Override
public int compareTo(Student another) {
/*
当前这个类和传来的类another进行比较,根据情况返回 负数 0 正数
*/
if (this.score<another.score)
return -1;
else if (this.score>another.score)
return 1;
return 0;
//return this.score - another.score
}
@Override
public boolean equals(Object student) {
/*
强制转换有可能出现异常,因此需要做出判断
*/
if (this == student)//比较当前类对象与传入的参数是否一致,如果一致,则不需要进行强制类型转换了,直接为true
return true;
if (student == null)//如果传入的对象为空的话,则直接为false即可
return false;
/*
如果当前的类对象与传入参数的类对象不属于同一个类的话,则直接为false,也不需要强制转换了
(之所以重写equals方法需要强制转换,是因为它的参数必须为类型Object,以此来涵盖所有可能传入的参数类型,
而如果具体传来的参数类型与。挣钱类对象不同的话,则这两个对象肯定是不同的)
*/
if (this.getClass() != student.getClass())
return false;
Student another = (Student) student;
return this.name.equals(another.name);//写比较逻辑
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", score=" + score +
'}';
}
}
主方法实现类:
public class SelectionSort {
public SelectionSort() {
}
//
public static <E extends Comparable<E>> void sort(E[] arr){
//arr[0...i)是有序的; arr[i...n) 是无序的s
for (int i = 0; i < arr.length; i++) {
//选择arr[i...n)中的最小值的索引
int minIndex = i;
for (int j = i;j < arr.length;j++){
//在剩余的元素中找到最小的(比较查找)
if (arr[j].compareTo(arr[minIndex]) < 0){
minIndex = j;
}
}
//将arr[i]与arr[minIndex]交换位置
swap(arr,i,minIndex);
}
}
private static <E> void swap(E[] arr, int i, int j) {
E t = arr[i];
arr[i] = arr[j];
arr[j] = t;
}
public static void main(String[] args) {
Integer[] arr = {1,4,2,3,6,5};
SelectionSort.sort(arr);
for (int item:arr){
System.out.print(item+" ");
}
System.out.println();
Student[] students = {new Student("Alice",98),
new Student("Bobo",100),
new Student("xiaoming",66)};
SelectionSort.sort(students);
for (Student student:students){
System.out.println(student+" ");
}
}
}
复杂度分析
除了两层循环以外,其余的操作都是常数级别的操作,其中在第二层循环当中,如果i为0的话,则需要进行n次操作,如果i=1的话,则需要进行n-1次操作,以此类推,一共需要1+2+3+...+n次操作。
首先在ArrayGenerator类当中生成随机数组
/*
因为是排序算法所以必须保证乱序,生成一个长度为n的随机数组,每个数字的范围是[0, bound)
*/
public static Integer[] generateRandomArray(int n,int bound){
Integer[] arr = new Integer[n];
Random rnd = new Random( );
for(int i = 0; i< n;i++)
arr[i] = rnd.nextInt(bound);
return arr;
}
判断这么大数组是否真的排序成功:
public class SortingHelper {
public SortingHelper() {
}
public static <E extends Comparable<E>> boolean isSorted(E[] arr){
//判断数组前一个元素是否小于后一个元素
for (int i = 1;i<arr.length;i++){
if (arr[i-1].compareTo(arr[i])>0)
return false;
}
return true;
}
}
在SortingHelper封装一个test方法用来测试任意一个排序方法:
//封装一个test方法用来测试任意一个排序方法
public static <E extends Comparable<E>> void sortTest(String sortname, E[] arr){
long startTime = System.nanoTime();
if(sortname.equals("SelectionSort"))
SelectionSort.sort(arr);
long endTime = System.nanoTime();
double time = (endTime - startTime) / 1000000000.0;
if(!SortingHelper.isSorted(arr))
throw new RuntimeException(sortname + "failed");
System.out.println(sortname+","+"n = "+arr.length+","+time +"s");
}
测试时间:
public class SelectionSort {
public SelectionSort() {
}
//
public static <E extends Comparable<E>> void sort(E[] arr){
//arr[0...i)是有序的; arr[i...n) 是无序的s
for (int i = 0; i < arr.length; i++) {
//选择arr[i...n)中的最小值的索引
int minIndex = i;
for (int j = i;j < arr.length;j++){
//在剩余的元素中找到最小的(比较查找)
if (arr[j].compareTo(arr[minIndex]) < 0){
minIndex = j;
}
}
//将arr[i]与arr[minIndex]交换位置
swap(arr,i,minIndex);
}
}
private static <E> void swap(E[] arr, int i, int j) {
E t = arr[i];
arr[i] = arr[j];
arr[j] = t;
}
public static void main(String[] args) {
int n = 10000;
Integer[] arr = ArrayGenerator.generateRandomArray(n,n);
SortingHelper.sortTest("SelectionSort", arr);
}
}
其中如果要测试两组数组:
public static void main(String[] args) {
int[] dataSize = {10000,100000};
for (int n:dataSize){
Integer[] arr = ArrayGenerator.generateRandomArray(n,n);
SortingHelper.sortTest("SelectionSort", arr);
}
}
可以看到由于n差了10倍,由于时间复杂度为O(n^2),所以最后时间差将近100倍。