常见排序算法
排序算法中几个概念
稳定性:两个相等的数在排序前后”相对位置”不变,则算法稳定。
稳定性得好处:从一个键上排序,然后再从另一个键上排序,第一个键排序的结果可以为第二个键排序所用。时间复杂度:对排序数据的总的操作次数。反映当n变化时,操作次数呈现什么规律。
T(n) = O(f(n)) // n 计算次数空间复杂度:是指算法在计算机内执行时所需存储空间的度量,它也是数据规模n的函数。
S(n) = O(f(n))各种算法稳定性
1、堆排序、快速排序、希尔排序、直接选择排序不是稳定的排序算法;
2、基数排序、冒泡排序、直接插入排序、折半插入排序、归并排序是稳定的排序算法。
1. 选择排序
算法描述:在未排序序列中找到最小(大)元素,将其存放到排序序列的起始位置,然后从剩余未排序元素中继续寻找最小(大)元素,然后放到已排序序列的末尾,
该算法在给定的数组中维护了连个子数组
1 | public static void selectionSort(int[] a) { |
时间复杂:O(n^2)
空间复杂度:O(1)
1 | void selectionSortStable(int[] a) { |
2. 冒泡排序
算法描述:冒泡排序是最简单的排序算法,如果它们的顺序错误,可以反复交换相邻的元素,重复进行直到没有交换。
1 | void bubbleSort(int[] a) { |
时间复杂:O(n^2)
空间复杂度:O(1)
上面实现 时间复杂度始终是 O(n^2)
其实当没有元素进行交互,说明已是有序数组,可以及时跳出循环。
递归实现
1 | void bubbleSortRecursive(int[] a, int n) { |
3. 插入排序
算法描述:通过构建有序序列,对于未排序数据,在已排序序列中从后向前扫描,找到相应位置并插入。
1 | void insertionSort(int[] a) { |
4. 归并排序
算法描述:该算法是采用分治法(Divide and Conquer)的一个非常典型的应用。将已有序的子序列合并,得到完全有序的序列;即先使每个子序列有序,再使子序列段间有序。若将两个有序表合并成一个有序表,称为2-路归并。
详细分解:把长度为n的输入序列分成两个长度为n/2的子序列;
对这两个子序列分别采用归排序;
将两个排序好的子序列合并成一个最终的排序序列。
图解如下:
1 | void mergeSort(int[] a, int l, int r) { |
5. 快速排序
算法描述:它选择一个元素作为枢轴(pivot),并围绕拾取的枢轴分割给定的数组。
详细分解:
从数组挑出一个元素,枢轴(pivot),可以始终选择第一个(最后一个|随机|中位数)元素作为枢轴。
- 分区,将比枢轴值小的摆放在枢轴前面,所有元素比枢轴值大的摆在枢轴的后面(相同的数可以到任一边)
递归地(recursive)把小于枢轴值元素的子数列和大于枢轴值元素的子数列排序。
图解:
1 | quickSort(int a[], int l, int r) { |