经典排序算法

文章目录

  • 经典排序算法
    • 1. 冒泡排序
    • 2. 选择排序
    • 3. 插入排序
    • 4. 希尔排序
      • 4.1 交换法实现希尔排序
      • 4.2 移动法实现希尔排序
    • 5. 快速排序

经典排序算法

  1. 排序算法的介绍
    排序也称排序算法(Sort Algorithm),排序是将一组数据,依指定的顺序进行排列的过程。
  2. 排序的分类
    1)内部排序:
    指将需要处理的所有数据都加载到内部存储器中进行排序。
    2)外部排序法:
    数据量过大,无法全部加载到内存中,需要借助外部存储进行排序。
    3)常见的排序算法分类(见右图):
    经典排序算法_第1张图片

1. 冒泡排序

  1. 基本介绍
    冒泡排序(Bubble Sorting)的基本思想是:通过对待排序序列从前向后(从下标较小的元素开始),依次比较相邻元素的值,若发现逆序则交换,使值较大的元素逐渐从前移向后部,就象水底下的气泡一样逐渐向上冒。
    优化:
    因为排序的过程中,各元素不断接近自己的位置,如果一趟比较下来没有进行过交换,就说明序列有序,因此要在排序过程中设置一个标志flag判断元素是否进行过交换。从而减少不必要的比较。(这里说的优化,可以在冒泡排序写好后,再进行)
  2. 演示冒泡过程的例子
    经典排序算法_第2张图片
    小结上面的图解过程:
    (1)一共进行数组的大小-1次大的循环
    (2)每一趟排序的次数在逐渐的减少
    (3)如果我们发现在某趟排序中,没有发生一次交换,可以提前结束冒泡排序。这个就是优化
  3. 代码实现
    有一群牛,颜值分别是101,34,119,1请使用选择排序从低到高进行排序[101,34,119,1]
    /**
     * 使用逐步推导的方式来编写冒泡排序
     * 冒泡排序并实现简单优化,时间复杂度O(n^2)
     * 80000数据进行排序大概需要:14秒
     * @param arr  传入需要排序的数组
     */
    private void bubbleSort(int[] arr) {
        int temp;
        boolean flag = false;   //表示变量,表示是否进行过交换
        for (int i = 0; i < arr.length - 1; i++) {
            for (int j = 0; j < arr.length - 1 - i; j++) {
                //将最大的元素排列在最后
                if (arr[j] > arr[j+1]) {
                    flag = true;    //优化部分
                    temp = arr[j];
                    arr[j] = arr[j+1];
                    arr[j+1] = temp;
                }
            }
    //            System.out.printf("第%d趟排序\n",i+1);
    //            System.out.println(Arrays.toString(arr));
            if (!flag) {    //如果没有进行过交换,直接暂停返回
                break;
            } else {
                flag = false;   //重置flag
            }
        }
    
    
        /*//第一趟排序,将最大的数排在最后面(要去arr.length-1个数相比较)
        int temp2 = 0;
        for (int i = 0; i < arr.length - 1; i++) {
            if (arr[i] > arr[i+1]) {
                temp2 = arr[i];
                arr[i] = arr[i+1];
                arr[i+1] = temp2;
            }
        }
        System.out.println("第一趟排序后的数组");
        System.out.println(Arrays.toString(arr));
    
        //第二趟排序,将最大的数排在最后面(要去arr.length-2个数相比较)
        int temp3 = 0;
        for (int i = 0; i < arr.length - 1 - 1; i++) {
            if (arr[i] > arr[i+1]) {
                temp2 = arr[i];
                arr[i] = arr[i+1];
                arr[i+1] = temp2;
            }
        }
        System.out.println("第一趟排序后的数组");
        System.out.println(Arrays.toString(arr));*/
    }
    

2. 选择排序

  1. 基本介绍
    选择式排序也属于内部排序法,是从欲排序的数据中,按指定的规则选出某一元素,再依规定交换位置后达到排序的目的。
  2. 选择排序思想
    选择排序(select sorting)也是一种简单的排序方法。它的基本思想是:第一次从arr[0]-arr[n-1]中选取最小值,与arr[0]交换,第二次从arr[1]-arr[n-1]中选取最小值,与arr[1]交换,第三次从arr[2]-arr[n-1]中选取最小值,与ar[2]
    交换,…,第i次从ar[i-1]-arr[n-1]中选取最小值,与ar[i-1]交换,……第n-1次从ar[n-2]-arr[n-1]中选取最小值,与arr[n-2]交换,总共通过n-1次,得到一个按排序码从小到大排列的有序序列。
  3. 选择排序思路分析图
    经典排序算法_第3张图片
  4. 思路
    原始的数组:101,34,119,1
    第一轮排序:1,34,119,101
    第二轮排序:1,34,119,101
    第三轮排序:1,34,101,119
  • 说明:
    • 1.选择排序一共有数组大小-1轮排序2.每1轮排序,又是一个循环,循环的规则(代)
    • 2.1先假定当前这个数是最小数
    • 2.2然后和后面的每个数进行比较,如果发现有比当前数更小的数,就重新确定最小数,并得到下标
    • 2.3当遍历到数组的最后时,就得到本轮最小数和下标
    • 2.4交换
  1. 代码实现
/**
 * 使用逐步推导的方式来编写选择排序
 * 选择排序:时间复杂度O(n^2)
 * 80000数据进行排序大概需要:3秒
 * @param arr 需要排序的数组
 */
private void selectSort(int[] arr) {
    int minIndex;   //数组中每一轮最小值的下标
    int min;    //数组中每一轮的最小值
    for (int i = 0; i < arr.length - 1; i++) {
        minIndex = i;
        min = arr[i];
        for (int j = i + 1; j < arr.length; j++) {
            if (min > arr[j]) {
                min = arr[j];   //每一轮重置min的值,使其成为最小值
                minIndex = j;   //每一轮重置minIndex的值,找到最小值的那个数组下标
            }
        }
        if (minIndex != i) {
            //将最小值与前面的arr[i]进行替换
            arr[minIndex] = arr[i];
            arr[i] = min;
        }
    }

    /*//第一轮
    int minIndex = 0;
    int min = arr[0];
    //与后面的arr.length-1个数相比较,选出最小的数放到最前面
    for (int i = 0 + 1; i < arr.length; i++) {
        if (min > arr[i]) {     //说明假定的最小值,并不是最小
            min = arr[i];       //重置min
            minIndex = i;       //重置minIndex
        }
    }
    //如果最小的数不是arr[0]就进行交换
    if (minIndex != 0) {
        //遍历过后min就是最小的值,i就是对应的最小值的坐标,与第一个位置进行交换
        arr[minIndex] = arr[0];
        arr[0] = min;
    }

    //第二轮
    minIndex = 1;
    min = arr[1];
    //与后面的arr.length-1个数相比较,选出最小的数放到最前面
    for (int i = 1 + 1; i < arr.length; i++) {
        if (min > arr[i]) {     //说明假定的最小值,并不是最小
            min = arr[i];       //重置min
            minIndex = i;       //重置minIndex
        }
    }
    //如果最小的数不是arr[0]就进行交换
    if (minIndex != 1) {
        //遍历过后min就是最小的值,i就是对应的最小值的坐标,与第一个位置进行交换
        arr[minIndex] = arr[0];
        arr[0] = min;
    }*/
}

3. 插入排序

  1. 插入排序法介绍
    插入式排序属于内部排序法,是对于欲排序的元素以插入的方式找寻该元素的适当位置,以达到排序的目的。
  2. 插入排序法思想:
    插入排序(Insertion Sorting)的基本思想是:把n个待排序的元素看成为一个有序表和一个无序表,开始时有序表中只包含一个元素,无序表中包含有n-1个元素,排序过程中每次从无序表中取出第一个元素,把它的排序码依次与有序表元素的排序码进行比较,将它插入到有序表中的适当位置,使之成为新的有序表。
  3. 插入排序思路图:
    经典排序算法_第4张图片
  4. 代码实现
    有一群小牛,考试成绩分别是101,34,119,1 请从小到大排序
private void insertSort(int[] arr) {

    /*
     * 实现思路参考下面的分布
     * 使用逐步推导的方式来编写插入排序:使用Debug可以观察其变化
     * 80000数据进行排序大概需要:1秒
     */
    for (int i = 1; i < arr.length; i++) {
        int insertVal = arr[i];
        int insertIndex = i - 1;
        while (insertIndex >= 0 && insertVal < arr[insertIndex]) {
            arr[insertIndex + 1] = arr[insertIndex];
            insertIndex --;
        }
        //当退出while循环时,说明插入位置找到,insertIndex+1
        if (insertIndex + 1 != i ) {     //说明并没有发生上面的while循环置换(插入排序优化)
            arr[insertIndex+1] = insertVal;
        }
    }

    /*//第一轮{34,101,119,1}
    //定义待插入的数
    int insertVal = arr[1];
    int insertIndex = 1 - 1;    //即arr[1]的前面这个数的下标
    //给insertVal找到插入的位置
    //说明
    //1.insertIndex>=0保证在给insertVal找插入位置,不越界
    // 2.insertVal= 0 && insertVal < arr[insertIndex]) {
        arr[insertIndex+1] = arr[insertIndex];
        insertIndex--;
    }
    //当退出while循环时,说明插入位置找到,insertIndex+1
    arr[insertIndex+1] = insertVal;
    System.out.println("第一轮排序");
    System.out.println(Arrays.toString(arr));

    //第二轮{34,101,119,1}
    //定义待插入的数
    int insertVal2 = arr[2];
    int insertIndex2 = 2 - 1;    //即arr[1]的前面这个数的下标
    //给insertVal找到插入的位置
    //说明
    //1.insertIndex>=0保证在给insertVal找插入位置,不越界
    // 2.insertVal= 0 && insertVal2 < arr[insertIndex2]) {
        arr[insertIndex2+1] = arr[insertIndex2];
        insertIndex2--;
    }
    //当退出while循环时,说明插入位置找到,insertIndex+1
    arr[insertIndex2+1] = insertVal2;
    System.out.println("第二轮排序");
    System.out.println(Arrays.toString(arr));

    //第三轮{1,34,101,119}
    //定义待插入的数
    int insertVal3 = arr[3];
    int insertIndex3 = 3 - 1;    //即arr[1]的前面这个数的下标
    //给insertVal找到插入的位置
    //说明
    //1.insertIndex>=0保证在给insertVal找插入位置,不越界
    // 2.insertVal= 0 && insertVal3 < arr[insertIndex3]) {
        arr[insertIndex3+1] = arr[insertIndex3];
        insertIndex3--;
    }
    //当退出while循环时,说明插入位置找到,insertIndex+1
    arr[insertIndex3+1] = insertVal3;
    System.out.println("第三轮排序");
    System.out.println(Arrays.toString(arr));*/
}

4. 希尔排序

  1. 希尔排序法介绍
    希尔排序是希尔(Donald Shell)于1959年提出的一种排序算法。希尔排序也是一种插入排序,它是简单插入排序经过改进之后的一个更高效的版本,也称为缩小增量排序。
  2. 希尔排序法基本思想
    希尔排序是把记录按下标的一定增量分组,对每组使用直接插入排序算法排序:随着增量逐渐减少,每组包含的关键词越来越多,当增量减至1时,整个文件恰被分成一组,算法便终止
  3. 希尔排序法的示意图
    经典排序算法_第5张图片
  4. 代码实现
    有一群小牛,考试成绩分别是{8,9,1,7,2,3,5,4,6,0} 请从小到大排序,请分别使用
    1)希尔排序时,对有序序列在插入时采用交换法,并测试排序速度
    2)希尔排序时,对有序序列在插入时采用移动法,并测试排序速度

4.1 交换法实现希尔排序

private void shellSort(int[] arr) {
    /**
     * 使用逐步推导的方式来编写希尔排序
     * 希尔排序:第一种方法:交换法
     * 80000数据进行排序大概需要:12秒
     */
    int temp = 0;
    //显示每次分几次组(arr.length/2表示第一次分多少组)
    for (int i = arr.length/2; i > 0; i /= 2) {    //5,2,1
        //i的值表示每一次分组所跨的步长
        for (int j = i; j < arr.length; j++) {  //i=2,j=2时,2,3,4,5,6,7,8,9
            for (int k = j - i; k >= 0; k -= i) {
                if (arr[k] >arr[k+i]) {
                    temp = arr[k];
                    arr[k] = arr[k+i];
                    arr[k+i] = temp;
                }
            }
        }
    }
//        System.out.println(Arrays.toString(arr));

    /*int temp = 0;
    //希尔排序的第1轮排序
    //因为第1轮排序,是将10个数据分成了5组
    for (int i = 10/2; i < arr.length; i++) {
        //遍历各组中所有的元素(共5组,每组有2个元素),步长为5
        for (int j = i - 5; j >= 0; j -= 5) {
            if (arr[j] > arr[j + 5]) {
                temp = arr[j];
                arr[j] = arr[j + 5];
                arr[j + 5] = temp;
            }
        }
    }
    System.out.println("希尔排序第一轮");      //[3, 5, 1, 6, 0, 8, 9, 4, 7, 2]
    System.out.println(Arrays.toString(arr));


    //希尔排序的第2轮排序
    //因为第1轮排序,是将10个数据分成了5/2 = 2组
    for (int i = 5/2; i < arr.length; i++) {
        //遍历各组中所有的元素(共2组,每组有5个元素),步长为2
        for (int j = i - 2; j >= 0; j -= 2) {
            if (arr[j] > arr[j + 2]) {
                temp = arr[j];
                arr[j] = arr[j + 2];
                arr[j + 2] = temp;
            }
        }
    }
    System.out.println("希尔排序第二轮");      //[0, 2, 1, 4, 3, 5, 7, 6, 9, 8]
    System.out.println(Arrays.toString(arr));*/
}

4.2 移动法实现希尔排序

/*
 * 使用移位法进行希尔排序:将希尔排序与插入排序相结合
 * 80000数据进行排序大概需要:1秒
 */
private void shellSort(int[] arr) {
    //增量gap
    for (int gap = arr.length / 2;gap > 0;gap /= 2) {
        //从第gap个元素,逐个对其所在的组进行直接插入排序
        for (int i = gap; i < arr.length; i++) {
            //使用插入排序的算法
            int j = i;
            int temp = arr[j];
            if (arr[j] < arr[j-gap]) {
                while (j - gap >= 0 && temp < arr[j-gap]) {
                    //移动
                    arr[j] = arr[j-gap];
                    j -= gap;
                }
                //当while循环退出时,就给temp找到插入的位置
                arr[j] = temp;
            }
        }
    }
//        System.out.println(Arrays.toString(arr));
}

5. 快速排序

  1. 快速排序法介绍
    快速排序(Quicksort)是对冒泡排序的一种改进。基本思想是:通过一趟排序将要排序的数据分割成独立的两部分,其中一部分的所有数据都比另外一部分的所有数据都要小,然后再按此方法对这两部分数据分别进行快速排序,整个排序过程可以递归进行,以此达到整个数据变成有序序列

  2. 快速排序法示意图:
    经典排序算法_第6张图片经典排序算法_第7张图片

  3. 代码实现
    要求:对[-9,78,0,23-567,70]进行从小到大的排序,要求使用快速排序法。【测试8w和800w】
    说明[验证分析]:
    1)如果取消左右递归,结果是 -9,-567,0,23,78,70
    2)如果取消右递归,结果是 -567,-9,0,23,78,70
    3)如果取消左递归,结果是 -9,-567,0,23,70,8

    /**
     * 快速排序:速度非常快,对8000000的大小数组进行排序只需要1秒钟
     * 此方法是以中间的那个只为基准
     * @param arr 排序的数组
     * @param left 左索引
     * @param right 右索引
     */
    private void quickSort(int[] arr,int left,int right) {
        int l = left;   //左下标
        int r = right;  //右下标
        //pivot 中轴值
        int pirvot = arr[(left + right) / 2];
        int temp = 0;   //临时变量
        //while循环的目的是让比pivot值小的放到左边,比pivot值大的放到右边
        while (l <r) {
            //在pivot左边一直找,直到找到大于等于pivot的值,才能退出
            while (arr[l] < pirvot) {
                l += 1;
            }
            //在pivot右边一直找,直到找到小于等于pivot的值,才能退出
            while (arr[r] > pirvot) {
                r -= 1;
            }
            //对上面的两个循环条件进行终止(当都左右索引已经遍历超过中间时,进行break)
            if (l >= r) {
                break;
            }
            //交换
            temp = arr[l];
            arr[l] = arr[r];
            arr[r] = temp;
            //如果交换完后,发现这个arr[l] == pivot值相等 r--,前移
            if (arr[l] == pirvot) {
                r -= 1;
            }
            //如果交换完后,发现这个arr[r] == pivot值相等 l++,后移
            if (arr[r] == pirvot) {
                l += 1;
            }
        }
        //如果 l == r,必须l++,r--,否则会出现栈溢出
        if (l == r) {
            l += 1;
            r -= 1;
        }
        //向左递归
        if (left < r) {
            quickSort(arr,left ,r );
        }
        //向右递归
        if (right > l) {
            quickSort(arr,l ,right );
        }
    }
    

你可能感兴趣的:(java数据结构与算法)