O平方级别的排序算法

希望疫情结束

今天开始,每天和大家分享一下学习数据结构和算法的新得和简单的案例,数据结构和算法是程序编程的内功。所以学好它至关重要。我希望可以和大家一起加持学下去,坚持将博客分享下去。
今天学习的是O(n^2)级别的排序算法----选择排序和直接插入排序
(默认情况下都是升序排序)
选择排序
在一堆待排的数据中,选择最小的元素,将其放在前面的排好序的位置,可能有点表达不清,举例说明:
比如我们有一个数组
【5,3,6,2,9】
第一轮我们找出元素中最小的元素2,将它和第一个元素交换位置
【2,3,6,5,9】
然后我们从第二个元素开始找最小的元素3,交换
从第三个元素开始找最小的元素5 交换
可以看出来选择排序就是每一次选择待排元素中的最小元素,然后放在前面排好序的元素的后面
如果还是不太明白过程,百度选择排序的动图,可以更加直观的看出
实现代码如下:
(当然为了显得高逼格一点 这里使用了泛型编程 以及可以比较自定义的对象类型)

package com.yxc.sort;

import com.yxc.domain.User;

import java.util.ArrayList;

/**
 * @Auther: 小新
 * @Date: 2020/2/7 09:06
 * @Description:选择排序实现 效率 O(n^2)
 * 思路:就是在一堆等待排列的元素中选择最小(最大)元素,将它与第一个元素交换位置,然后找到第二小(第二大)
 * 元素,然后让它与第二个元素交换,以此类推下去就可以 代码实现如下
 * 使用泛型编程,注意main函数中的a数据如果是整形必须定义成Integer,不能使用int,不然会出现
 */
public class  SelectedSort {
    public static  void main(String[] args) {
        Integer a[]={2,5,4,3,1,6,0,21,56,4};
        Double b[]={2.0,5.2,6.8,8.5,6.88889,6.88888};
        selectSort(a);
        selectSort(b);
        //自定义类型的排序测试
        //有几个对象就创建几个对象的数组,不然会出现空指针异常
        User[] user=new User[5];
        user[0]=new User(1,"yxc",26);
        user[1]=new User(1,"yxc1",25);
        user[2]=new User(2,"yxc2",23);
        user[3]=new User(9,"yxc3",23);
        user[4]=new User(4,"yxc4",23);
        selectSort(user);
        for(int i=0;i<user.length;i++){
            System.out.println(user[i]);
        }
        for(int i=0;i<a.length;i++){
            System.out.print(a[i]+" ");
        }
        System.out.println();
        for(int i=0;i<b.length;i++){
            System.out.print(b[i]+" ");
        }

    }

    /***
     * 选择排序 升序
     * @param a
     */
    public static  void selectSort(Comparable a[]){
        int n=a.length;
        //寻找[i,n]最小的元素 其中n为数字长度
        for(int i=0;i<n;i++){
            //记录最小元素的下标
            int index=i;
            for(int j=i+1;j<n;j++){
                if(a[j].compareTo(a[index])<0){
                    index=j;
                }
            }
            swap(a,i,index);
        }
    }
    //交换两个元素
    public static void swap(Object a[],int i,int j){
        Object temp=a[i];
        a[i]=a[j];
        a[j]=temp;
    }
}

User.java

package com.yxc.domain;

/**
 * @Auther: 小新
 * @Date: 2020/2/7 10:06
 * @Description: 对自定义的对象类型排序
 */
public class User implements  Comparable<User>{
    private Integer id;
    private String usernmae;
    private Integer age;
    //提供构造方法
    public User(){}
    public User(Integer id, String usernmae, Integer age) {
        this.id = id;
        this.usernmae = usernmae;
        this.age = age;
    }

    /***
     * 对用户排序,默认按ID的升序排列
     * @param user
     * @return
     */
    @Override
    public int compareTo(User user) {
        if(this.id<user.id){
            return -1;
        }else if(this.id>user.id){
            return 1;
        }else{
            //如果id相同就按照年龄排序
            return this.age.compareTo(user.age);
        }
    }
    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getUsernmae() {
        return usernmae;
    }

    public void setUsernmae(String usernmae) {
        this.usernmae = usernmae;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    @Override
    public String toString() {
        return "User{" +
                "id=" + id +
                ", usernmae='" + usernmae + '\'' +
                ", age=" + age +
                '}';
    }
}

介绍我们的第二种的排序算法 直接插入排序
其实生活中我们都使用过这种排序,就是打牌的时候,我们会习惯性的将牌排好序放,这个排序就类型与直接插入排序
排序原理:
我们循环每一个元素,然后将它放在前面合适的位置,这样看来,我们可以将所有数据分为两堆,排好序的和没有排好序的,每一次从没有排好序中取一个元素,将它插入到排好序的位置,而且保证按照正确的排好序
同样的例子说明问题:
【8,1,6,5,9】
首先第一个元素8默认就是排好序的。所以我们从第二个元素开始
拿出1,它比8小,所以我们要交换位置
【1,8,6,5,9】
拿出6,它比8小,交换位置,继续比较,比1大。不用交换
【1,6,8,5,9】
拿出5 比8小,交换,继续比较,比6小交换,继续比较 比1大,结束比较
以此类推下去:
另外还有一种简单的优化方式,就是每一次还需要交换,我们可以取出这个值,一直比较,直接放在合适的位置,这样在数据很大的时候效率会高一点,下面通过代码来实现算法并且比较一下效率问题

package com.yxc.sort;

import com.yxc.utils.CommonUtils;

/**
 * @Auther: 小新
 * @Date: 2020/2/7 15:02
 * @Description:直接插入排序 O(n^2)
 * 思想:循环遍历数组,将每一个元素放在前面排好序的合适的位置
 */
public class InsertionSort {
    public static void main(String[] args) {
        /*Integer[] arry={1,8,6,5,2,3};
        insertion2(arry);
        CommonUtils.printArray(arry);*/
        //从测试结果可以看出来,当数据量很大的情况下,第二种方式还是更加高效一点的
        Integer[] arry = CommonUtils.createRandom(10000);
        //对两种排序效率的测试
        insertion(arry);
        insertion2(arry);
        //测试结果:直接插入排序使用时间210ms
        //          优化以后的直接插入排序的时间为3ms
    }

    /***
     * 直接插入排序
     * @param arry
     */
    public static void insertion(Comparable[] arry){
        long start = System.currentTimeMillis();
        int length=arry.length;
        //从第二个元素开始,第一个元素是排好序的
        for(int i=1;i<length;i++){
            //如果当前元素比前一个元素小,那么交换元素,然后开始继续往前比较
            //如果当前元素大于前一个元素,就可以结束本次循环
            for(int j=i;j>0&&(arry[j].compareTo(arry[j-1])<0);j--){
                if(arry[j].compareTo(arry[j-1])<0){
                    CommonUtils.swap(arry,j,j-1);
                }else{
                    break;
                }
            }
        }
       long end=System.currentTimeMillis();
        System.out.println("直接插入排序使用时间"+(end-start)+"ms");
    }

    /***
     * 直接插入的简单优化
     * @param arry
     */
    public static void insertion2(Comparable[] arry){
        long start=System.currentTimeMillis();
        int length=arry.length;
        for(int i=1;i<length;i++){
            //不用交换元素
            Comparable temp=arry[i];
           for(int j=i;j>0&&(arry[j].compareTo(arry[j-1])<0);j--){
                if(temp.compareTo(arry[j-1])<0){
                    arry[j]=arry[j-1];
                }else{
                    arry[j]=  temp;
                    break;
                }
            }
        }
        long end=System.currentTimeMillis();
        System.out.println("优化以后的直接插入排序的时间为"+(end-start)+"ms");
    }

}

CommonUtils.java

package com.yxc.utils;

import java.util.Random;

/**
 * @Auther: 小新
 * @Date: 2020/2/7 10:37
 * @Description:一些常用简单的的工具类
 */
public class CommonUtils {
    /***
     * 交换数组中两个元素的值
     * @param arry
     * @param i
     * @param j
     */
    public static void swap(Object arry[],int i,int j){
        Object temp=arry[i];
        arry[i]=arry[j];
        arry[j]=temp;
    }

    /***
     * 打印数组
     * @param arry
     */
   public static void printArray(Object[] arry){
        for(int i=0;i<arry.length;i++){
            System.out.print(arry[i]+"  ");
        }
   }

    /***
     * 产生由随机数组成的数组
     * @param n
     */
   public static Integer[] createRandom(int n){
       Integer[] arry=new Integer[n];
       Random ran=new Random();
       //如果是下面的情况,每一次产生的随机数就一样
       //Random ran=new Random(1);
       for(int i=0;i<n;i++){
           arry[i]=ran.nextInt(1000);
       }
       return arry;
   }

}

一些具体的代码中都有详细的注释。今天学习的两种算法都是O(n^2)级别的算法,当然这种级别的算法还有一个更加经典的就是冒泡排序,相信每一本书上第一个排序算法都会拿它举例子,大家可以自行学习。如果一个数组基本是有序的,那么我们使用直接插入排序基本可以达到O(n)级别的效率,注意我们在条件中有(arry[j].compareTo(arry[j-1])<0),如果基本有序那么可以直接跳出内循环。今天的学习就到这里结束,疫情期间,在此督促自己不断的学习。更希望疫情早点结束。为自己加油,为祖国加油!!!

发布了33 篇原创文章 · 获赞 37 · 访问量 4370

猜你喜欢

转载自blog.csdn.net/weixin_42142899/article/details/104211454