算法与数据结构(第二周)——排序基础:选择排序法

目录

选择排序

选择排序简单介绍

实现选择排序法

使用带约束的泛型

使用 Comparable 接口

复杂度分析


选择排序

选择排序简单介绍

先把最小的拿出来

剩下的,再把最小的拿出来

剩下的,再把最小的拿出来

......

每次选择还没处理的元素里最小的元素

        我们每一次找剩下的元素中最小的元素,我们只需要把这最小的元素直接放在数组的开头就行了,也就是直接利用当前的数组的空间,就可以实现原地排序。

        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倍。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值