动画详解十大经典排序算法【Java版实现】(上)

经典排序算法可以说是面试必考题,然而大学毕业之后基本都忘光了,正好趁这个机会拿出来好好总结复习下,也算不辜负当年老师的一片苦心了。

十大经典排序算法包括:冒泡排序,选择排序,插入排序,希尔排序,归并排序,快速排序,堆排序,计数排序,桶排序,基数排序

本来准备一口气把十种都介绍一遍,然而整理完前五种后大脑就宕机了(亲测熬夜会使人变傻······),那干脆就分为上下两部分好了,正好也方便记忆。

接下来我们用动画的方式,先来分析前五种算法:冒泡排序,选择排序,插入排序,希尔排序,归并排序


1. 冒泡排序

算法介绍

冒泡排序是一种简单的排序算法,它会重复遍历要排序的数列,一次比较两个元素,如果两个元素的顺序错误,就交换过来。

遍历数列需要重复进行,直到没有再需要交换的元素,才代表数列已经排序完成。

因为小的元素会经由交换慢慢“浮”到数列的顶端,就像水泡冒出来一样,所以才称之为“冒泡排序”。

动画演示

步骤描述

  • 比较相邻的元素:如果第一个元素比第二个大,就交换它们;
  • 比较每一对相邻元素并交换,从开始第一对到最后一对,这样最后的元素就是最大的数;
  • 针对所有的元素重复以上步骤,除了最后一个;
  • 重复前三个步骤,直到排序完成;

代码实现

    public static void main(String[] args) {
        int arr[] = {8, 5, 3, 2, 4};
        //冒泡
        for (int i = 0; i < arr.length; i++) {
            //外层循环,遍历次数
            for (int j = 0; j < arr.length - i - 1; j++) {
                //内层循环,升序(如果后一个值比前一个值小,则交换)
                //内层循环一次,获取一个最大值
                if (arr[j + 1] < arr[j]) {
                    int temp = arr[j + 1];
                    arr[j + 1] = arr[j];
                    arr[j] = temp;
                }
            }
        }
    }

2. 选择排序

算法介绍

选择排序是一种简单直观的排序算法。

它首先在未排序序列中找到最小(大)元素,存放到排序序列的起始位置,然后,再从剩余未排序元素中继续寻找最小(大)元素放到已排序序列的末尾。

以此类推,直到所有元素全部排序完毕。

动画演示

步骤描述

  • 将第一个值看成最小值;
  • 和后续的值进行比较,找出最小值和下标;
  • 交换本次遍历的起始值和最小值;
  • 重复以上步骤直到列表排序结束;

代码实现

    public static void main(String[] args) {
        int arr[] = {6, 5, 3, 2, 4};
        // 选择
        for (int i = 0; i < arr.length; i++) {
            // 默认第一个是最小的
            int min = arr[i];
            // 记录最小的下标
            int index = i;
            // 通过与后面的数据进行比较得出,最小值和下标
            for (int j = i + 1; j < arr.length; j++) {
                if (min > arr[j]) {
                    min = arr[j];
                    index = j;
                }
            }
            //然后将最小值与本次循环的a[i]值进行交换
            arr[index] = arr[i];
            arr[i] = min;
        }
    }

3. 插入排序

算法介绍

对于未排序数据,在已排序序列中从后向前扫描,找到相应位置并插入。

插入排序在从后向前扫描过程中,需要反复把已排序元素逐步向后挪位,为最新元素提供插入空间。

动画演示

步骤描述

  • 第一个元素默认已经被排序;
  • 取出下一个元素,在已经排序的元素序列中从后向前扫描;
  • 如果该元素大于新元素,将该元素移到下一位置;
  • 重复上一步骤,直到找到已排序的元素<=新元素的位置;
  • 将新元素插入到该位置;
  • 重复步骤 2 ~ 5;

代码实现

    public static void main(String[] args) {
        int arr[] = {7, 5, 3, 2, 4};
        //插入排序
        for (int i = 1; i < arr.length; i++) {
            //外层循环,从第二个开始比较
            for (int j = i; j > 0; j--) {
                //内层循环,与前面排好序的数据比较,如果后面的数据小于前面的则交换
                if (arr[j] < arr[j - 1]) {
                    int temp = arr[j - 1];
                    arr[j - 1] = arr[j];
                    arr[j] = temp;
                } else {
                    //如果不小于,说明插入完毕,退出内层循环
                    break;
                }
            }
        }
    }

4. 希尔排序

算法介绍

希尔排序也是一种插入排序,它是简单插入排序经过改进之后的一个更高效的版本,也称为缩小增量排序。它与插入排序的不同之处在于,它会优先比较距离较远的元素。

希尔排序会把记录按下表的一定增量分组,对每组使用直接插入排序算法排序;随着增量逐渐减少,每组包含的关键词越来越多,当增量减至1时,整个文件恰被分成一组,算法便终止。

动画演示

动画演示效果看起来不太好理解,我们可以通过以下图片进一步加深理解: 

步骤描述

  • 选择一个增量序列 t1,t2,……,tk,其中 ti > tj, tk = 1;

  • 按增量序列个数 k,对序列进行 k 趟排序;

  • 每趟排序,根据对应的增量 ti,将待排序列分割成若干长度为 m 的子序列,分别对各子表进行直接插入排序;

  • 仅增量因子为 1 时,整个序列作为一个表来处理,表长度即为整个序列的长度;

代码实现

    public static void main(String[] args) {
        int arr[] = {7, 5, 3, 2, 4};
        //希尔排序(插入排序变种版)
        for (int i = arr.length / 2; i > 0; i /= 2) {
            //i层循环控制步长
            for (int j = i; j < arr.length; j++) {
                //j控制无序端的起始位置
                for (int k = j; k > 0  && k - i >= 0; k -= i) {
                    if (arr[k] < arr[k - i]) {
                        int temp = arr[k - i];
                        arr[k - i] = arr[k];
                        arr[k] = temp;
                    } else {
                        break;
                    }
                }
            }
            //j,k为插入排序,不过步长为i
        }
    }

5. 归并排序

算法介绍

归并排序是建立在归并操作上的一种有效的排序算法。

该算法是采用分治法(Divide and Conquer)的一个非常典型的应用,是一种稳定的排序方法。

排序规则是将已经有序的子序列进行合并,得到完全有序的序列;即先使每个子序列有序,再使子序列段间有序。若将两个有序表合并成一个有序表,称为2-路归并。 

动画演示

步骤描述

  • 把长度为n的输入序列分成两个长度为n/2的子序列;
  • 对这两个子序列分别采用归并排序;
  • 将两个排序好的子序列合并成一个最终的排序序列;

代码实现

    public static void main(String[] args) {
        int arr[] = {7, 5, 3, 2, 4, 1,6};
        //归并排序
        int start = 0;
        int end = arr.length - 1;
        mergeSort(arr, start, end);
    }

    public static void mergeSort(int[] arr, int start, int end) {
        //判断拆分的不为最小单位
        if (end - start > 0) {
            //再一次拆分,直到拆成单个数据
            mergeSort(arr, start, (start + end) / 2);
            mergeSort(arr, (start + end) / 2 + 1, end);
            //记录开始/结束位置
            int left = start;
            int right = (start + end) / 2 + 1;
            //记录每个小单位的排序结果
            int index = 0;
            int[] result = new int[end - start + 1];
            //如果拆分后的两块数据都还存在
            while (left <= (start + end) / 2 && right <= end) {
                //比较两块数据的大小,然后赋值,并且移动下标
                if (arr[left] <= arr[right]) {
                    result[index] = arr[left];
                    left++;
                } else {
                    result[index] = arr[right];
                    right++;
                }
                //移动单位记录的下标
                index++;
            }
            //当某一块数据不存在
            while (left <= (start + end) / 2 || right <= end) {
                //直接赋值到记录下标
                if (left <= (start + end) / 2) {
                    result[index] = arr[left];
                    left++;
                } else {
                    result[index] = arr[right];
                    right++;
                }
                index++;
            }
            //最后将新的数据赋值给原来的列表,并且是对应分块后的下标
            for (int i = start; i <= end; i++) {
                arr[i] = result[i - start];
            }
        }
    }

看到归并排序脑子就不行了,本篇博客就到这里,我们下期再见。


参考了博客如下,在这里表示特别感谢:

Java中的十大经典排序算法最强总结!!!_良月柒-CSDN博客

Java的几种常见排序算法 - 小不点丶 - 博客园

Guess you like

Origin blog.csdn.net/j1231230/article/details/120672576