【算法】排序(1),冒泡排序,选择排序,插入排序

大家好,我是被白菜拱的猪。

一个热爱学习废寝忘食头悬梁锥刺股,痴迷于girl的潇洒从容淡然coding handsome boy。

一、写在前言

大学已经两年了,至今对各种排序仍不熟悉,光听说什么希尔排序,归并排序,他们到底是个啥却丝毫不知,有时候恨不得挖个洞钻进去。之前不知道也就算了,别让自己永远不知道。

有人在学排序的时候会出现一个问题,学了之后不久就忘掉了,知道有这么一个东西,但最终怎么写却不知道,我认为有两种原因,一没有深刻的了解各个排序算法的流程,也就是计算机是如何将数据排序的,二是没有把握好细节,知道思路了但不知道怎么敲,往往是有些细节没有把握好。

我们要做的是记住的是计算机是如何排的,以及他们之间的差别,人的大脑在记东西时,图片往往比文字更容易记住,所以我这里推荐一个可视化网站可以帮助我们记忆:

数据结构和算法动态可视化

好了闲话不多说,让我们开启排序的旅程。

二、排序

(一)简单介绍

在具体介绍排序之前,我们先要了解了解排序有多少种,先在宏观上把握好。

在接下来我们要讲八种排序算法,冒泡排序,直接插入排序,简单选择排序属于简单算法,希尔排序,堆排序,归并排序,快速排序属于改进算法,然后在加一个基数排序(桶排序)。

在这里插入图片描述
除了了解什么排序之外,还要了解排序算法的时间复杂度,从而晓得他们哪个牛逼不牛逼。

下面简单介绍几种常见的时间复杂度(我现在对此还此是云里雾里,所以更要好好学啊)

  • O(logn)
int i = 1
while(i<n) {
	i = i *2;
}
  • O(n)单层的for循环
  • O(nlogn)就是将O(logn)执行n遍,就是线性对数阶
for(int m = 1; m < n ;m++) {
	i = 1;
	while(i < n) {
		i = i * 2;
	}
}

常见的算法时间复杂度由小到大依次为:Ο(1)<Ο(log2n)<Ο(n)<Ο(nlog2n)<Ο(n2)<Ο(n3)< Ο(n^k) <Ο(2^n) ,随着问题规模n的不断增大,上述时间复杂度不断增大,算法的执行效率越低。

这里对算法的时间复杂度是什么,还有什么事前分析和事后统计就不在过多的介绍。那我们直接开始吧。

(二)冒泡排序

1、思路图解

在这里插入图片描述
假如图解没看懂的话建议大家一定要去我推荐的网站去操作操作,一定要了解他的原理,因为后面还要讲七种排序算法,搞不好会弄混,一定要想一想他们之间的区别,以及为什么会叫这个名字,冒泡,就像小泡泡一样咕咕咕的升到水面。

另外,我们发现假如直接是 1 2 3 4 5时,已经是排好顺序的,但是它还是要进行比较,这种比较就是多余的,所以我们可以将代码进行优化。

冒泡排序的时间复杂度是O(n^2)

2、代码实现

package com.codingboy.sort;

import java.lang.reflect.Array;
import java.util.Arrays;
import java.util.Random;

/**
 * @author: ljl
 * @date: 2020/8/11 21:40
 * @description: 冒泡排序
 */
public class BubbleSort {
    public static void main(String[] args) {
//        int[] arr = {5, 10 ,-1 , 3 ,2 };
//        bubbleSort(arr);
//        System.out.println(Arrays.toString(arr));

        //下面对八万条数据进行测试
        int[] arr = new int[80000];
        for (int i = 0; i < arr.length; i++) {
            arr[i] = (int)(Math.random() * 80000); //Math.random [0,1)
        }
        long before = System.currentTimeMillis();
        bubbleSort(arr);
        long after = System.currentTimeMillis();
        System.out.println("用时(s):" + (after - before)/1000);

    }

    //单独封装成一个方法,并且使用flag来优化,以防已排好进行重复判断
    public static void bubbleSort(int[] arr) {
        int temp = 0;
        boolean flag = false;//表示没有进行交换
        //先把握好大体的方向,要进行arr.length - 1 趟排序,才能把各个数字放在准确的位置
        for (int i = 0; i < arr.length -1; i++) {
            //每一趟排序,第一趟要进行4次交换,第二趟要进行3次交换
            for(int j = 0;j < arr.length - 1 -i;j++) {
                //交换
                if(arr[j] > arr[j+1]) {
                    //假如进项了交换,就将flag = true
                    flag = true;
                    temp = arr[j+1];
                    arr[j+1] = arr[j];
                    arr[j] = temp;
                }
            }
            //一趟下来没有交换位置,说明就已经排好了顺序
            if(!flag) {
                break;
            }else {
                flag = false;
            }
        }
    }
}

这里将冒泡单独封装成了一个方法,而且对此进行了优化,除此之外又对八万条随机数据进行了排序排序,用时约十秒钟。
在这里插入图片描述

(三)选择排序

1、思路图解

在这里插入图片描述
选择排序(select sorting)也是一种简单的排序方法。它的基本思想是:第一次从arr[0]~ arr[n-1]中选取最小值,与arr[0]交换,第二次从arr[1]~ arr[n-1]中选取最小值,与arr[1]交换,第三次从arr[2]~ arr[n-1]中选取最小值,与arr[2]交换,…,第i次从arr[i-1]~ arr[n-1]中选取最小值,与arr[i-1]交换,…, 第n-1次从arr[n-2]~arr[n-1]中选取最小值,与arr[n-2]交换,总共通过n-1次,得到一个按排序码从小到大排列的有序序列。

2、代码实现

我们发现要使用两个for循环,外面的一层是代表进行几轮排序,每一轮排序就确定好一个位置,五个数字,就进行4轮排序,每一轮排序就要去判断谁最小,第一轮,假如第一个数字最小,那么就要将当前最小的数与后面的数一一判断,从而得到最小的数,也就是要进行四次判断,同理第二轮要进行三次判断。talk is shit,show me the code。

package com.codingboy.sort;

import java.util.Arrays;

/**
 * @author: ljl
 * @date: 2020/8/12 9:33
 * @description: 选择排序
 */
public class SelectSort {
    public static void main(String[] args) {
        int[] arr1 = {109, 1, 10, 5};
        System.out.println(Arrays.toString(arr1));
        selectSort(arr1);
        System.out.println(Arrays.toString(arr1));

        //下面对八万条数据进行测试
        int[] arr = new int[80000];
        for (int i = 0; i < arr.length; i++) {
            arr[i] = (int)(Math.random() * 80000); //Math.random [0,1)
        }
        long before = System.currentTimeMillis();
        selectSort(arr);
        long after = System.currentTimeMillis();
        System.out.println("用时(s):" + (after - before)/1000.0);

    }

    public static void selectSort(int[] arr) {
        //用来记录最小值
        int min = 0;
        //用来记录最小值的索引,方便日后交换位置
        int index = 0;
        for (int i = 0; i < arr.length - 1; i++) {
            min = arr[i];
            index = i;
            //下面进行判断,选择出最小值与开始位置交换
            for (int j = i + 1;j < arr.length;j++) {
                if (min > arr[j]) {
                    min = arr[j];
                    index = j;
                }
            }
            //比较结束,交换位置,假如最小值还是原先的那个值即index没有变,就不用交换
            if(index != i){
                arr[index] = arr[i];
                arr[i] = min;
            }
        }

    }
}

同样我们对选择排序也进行了八万条数据测试,发现选择排序快于冒泡排序,这是因为选择排序每次循环只进行了一次交换,而冒泡则交换多次。而选择排序的时间复杂度与冒泡排序相同都是O(n^2)。
在这里插入图片描述

(四)插入排序

1、思路讲解

插入排序是将待排序的n个元素分为两个部分,有序表和无序表,有序表一开始只有一个元素,无序表则有n-1个元素,我们接下来要做的就是将无序表中的元素一一插进有序表。
插入的关键在于如何找到合适的位置,在讲排序时,我们都是按照升序讲解,首先将要插入的元素与前面的元素进行比较,假如小于前面的元素,则将前面的元素后移,知道前面的元素小于要插入的元素。说的有些抽象直接上代码。

2、代码实现

package com.codingboy.sort;

import java.util.Arrays;

/**
 * @author: ljl
 * @date: 2020/8/12 10:55
 * @description: 插入排序
 */
public class InsertSort {
    public static void main(String[] args) {
        int[] arr1 = {101, 34, 119 , 1 ,1};
        System.out.println(Arrays.toString(arr1));
        insertSort(arr1);
        System.out.println(Arrays.toString(arr1));

        //下面对八万条数据进行测试
        int[] arr = new int[80000];
        for (int i = 0; i < arr.length; i++) {
            arr[i] = (int)(Math.random() * 80000); //Math.random [0,1)
        }
        long before = System.currentTimeMillis();
        insertSort(arr);
        long after = System.currentTimeMillis();
        System.out.println("八万条数据测试用时(s):" + (after - before)/1000.0);

    }

    public static void insertSort(int[] arr) {
        int insertValue = 0; //要插入的元素
        int insertIndex = 0; //要插入的索引值
        for (int i = 1; i < arr.length; i++) { //这里不需要-1,有序表默认只有第一个元素,所以我们需要将后面的元素一一插入
            insertValue = arr[i]; //默认插入的元素为当前元素
            insertIndex = i - 1; //默认要插入的地方为当前元素前面的位置
            //假如前面的比他大,则后移
            while (insertIndex >= 0 && arr[insertIndex] > insertValue){//防止越界
               //后移
                arr[insertIndex + 1] = arr[insertIndex];
                insertIndex--;
            }
            //最后插入指定的位置,即insertIndex + 1,因为前面while中最后insertIndex--了。减过的要给他加回去
            arr[insertIndex + 1] = insertValue;
        }
    }
}

最后八万条数据测试,卧槽好快啊
在这里插入图片描述
时间复杂度同样是O(n^2)

三、结束语

这里有一个问题,为什么同样的时间复杂度,执行的时间插入排序要快那么多?

代码上看,冒泡排序的数据交换比插入排序的数据移动要复杂,冒泡排序需要三次赋值操作,而插入排序只需要一次。这也是插入排序受欢迎的原因之一。

这里就先讲讲我们对此不陌生的三个排序,后面讲希尔,归并等不熟悉的排序算法。

我依稀的记得大一上c++考试的时候考了一道选择排序,当时一脸懵逼,书上原封不动的代码,但是我当时看了冒泡,觉得选择不会考,结果他偏偏就考了,最后瞎写的,现在想想当初是真的菜啊,但是我们每一个人不都是从菜一步步过来的嘛。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值