前言
排序就是将一组对象按照某种逻辑顺序重新排列的过程。排序在数据处理和现代科学计算中有很重要的地位,应用于很多领域。排序问题一直是程序员工作与面试的重点,今天特意整理研究下与大家共勉。本文将介绍一下常见的排序算法以及 Java 代码实现,如有问题,欢迎指正!
各算法原理及代码实现
冒泡排序(Bubble Sort)
冒泡排序(Bubble Sort)是一种简单的排序算法。它重复地走访要排序的数列,一次比较两个元素,如果他们的顺序错误就把他们交换过来。走访数列的工作是重复地进行直到没有再需要交换,也就是说该数列已经排序完成。这个算法的名字由来是因为越小的元素会经由交换慢慢 “浮” 到数列的顶端。
原理
- 比较相邻的元素。如果第一个比第二个大,就交换他们两个。
- 对每一对相邻元素作同样的工作,从开始第一对到结尾的最后一对。在这一点,最后的元素应该会是最大的数。
- 针对所有的元素重复以上的步骤,除了最后一个。
- 持续每次对越来越少的元素重复上面的步骤,直到没有任何一对数字需要比较。
代码实现
1 | public static void bubbleSort(int[] numbers) { |
快速排序(Quick Sort)
选择一个基准元素,通常选择第一个元素或者最后一个元素,通过一趟扫描,将待排序列分成两部分,一部分比基准元素小,一部分大于等于基准元素,此时基准元素在其排好序后的正确位置,然后再用同样的方法递归地排序划分的两部分。
快速排序
1 | public static void quickSort(int[] numbers, int low, int high) { |
三向切分快速排序
1 | public static void quickSort3Way(int[] numbers, int low, int high) { |
选择排序(Selection Sort)
直接选择排序是一种简单直观的排序算法。它的工作原理如下:首先在未排序序列中找到最小元素,存放到排序序列的起始位置,然后,再从剩余未排序元素中继续寻找最小元素,然后放到排序序列末尾(目前已被排序的序列)。以此类推,直到所有元素均排序完毕。
原理
假设数据放在一个数组 a 中,且数组的长度是 N,则直接选择排序的流程为:
- 从 a[0]-a[N-1]中选出最小的数据,然后与 a[0]交换位置
- 从 a[1]-a[N-1]中选出最小的数据,然后与 a[1]交换位置(第 1 步结束后 a[0]就是 N 个数的最小值)
- 从 a[2]-a[N-1]中选出最小的数据,然后与 a[2]交换位置(第 2 步结束后 a[1]就是 N-1 个数的最小值)
- 以此类推,N-1 次排序后,待排数据就已经按照从小到大的顺序排列了。
代码实现
1 | public static void selectionSort(int[] numbers) { |
堆排序(Heap Sort)
堆排序算法和直接选择排序算法最大的不同在于,堆排序算法充分利用大顶堆和完全二叉树的性质,保留每次排序后的结构,同时由于每次比较只是比较根节点和它的子节点,因此大大降低了比较的次数和交换的次数,从而提高效率。
原理
假设数据放在一个数组 a 中,且数组的长度是 N:
- 以数组 a 为数据,建立一个大顶堆(这样对于二叉树的每个节点,根节点总是比子节点大,其实没必要要求二叉树的每个子树也是大顶堆)
- 交换大顶堆的根节点和数组 a 中的最后一个节点(最后一个节点不在参与后边的工作)
- 重复上边的工作,经过 N-1 次后,数组 a 已经排好序。
代码实现
1 | public static void heapSort(int[] numbers) { |
插入排序(Insertion Sort)
插入排序是一种通过不断地把新元素插入到已排好序的数据中的排序算法,常用的插入排序算法包括直接插入排序和希尔(Shell)排序,直接插入排序实现比较简单,但是直接插入没有充分的利用已插入的数据已经排序这个事实,因此有很多针对直接插入排序改进的算法。
原理
在要排序的一组数中,假设前面 (n-1)[n>=2] 个数已经是排好顺序的,现在要把第 n 个数插到前面的有序数中,使得这 n 个数也是排好顺序的。如此反复循环,直到全部排好顺序。
也就是说,先从无序区拿第一个记录出来,它是有序的,然后把无序区中的记录一个一个插入到其中,那么插入之后是有序的,所以直到最后都是有序的。
代码实现
1 | public static void insertionSort(int[] numbers) { |
希尔排序(Shell Sort)
希尔排序是插入排序的一种。是针对直接插入排序算法的改进。该方法又称缩小增量排序,因 DL.Shell 于 1959 年提出而得名。
原理
先取定一个小于 n 的整数 d1 作为第 1 个增量,把文件的全部记录分成 d1 个组,所有距离为 d1 的倍数的记录放在同一个组中,在各组内进行直接插入排序;然后,取第 2 个增量 d2<d1 重复上述的分组和排序,直至所取的增量 d1=1(dt<dt-1<…<d2<d1),即所有记录放在同一组中进行直接插入排序为止。
代码实现
1 | public static void shellSort(int[] numbers) { |
归并排序(Merge Sort)
归并排序是建立在归并操作上的一种有效的排序算法, 该算法是采用分治法(Divide and Conquer)的一个非常典型的应用。
原理
归并(Merge)排序法是将两个(或两个以上)有序表合并成一个新的有序表,即把待排序序列分为若干个子序列,每个子序列是有序的。然后再把有序子序列合并为整体有序序列。
代码实现
1 | public static void mergeSort(int[] numbers, int low, int high) { |