1.概念
1.1 排序
排序,就是让一串记录,按照其中某个或者某些关键字的大小,递增或递减的排列起来的操作。平时上下文中,如果提到排序,通常升序排列。
1.2 稳定性
两个相等的数据,如果排序后,排序算法能保证其相对位置不会发生变化,则我们就称该算法是稳定的排序算法。
2.基于比较的常用排序算法
常用排序算法:插入排序(直接插入排序、希尔排序),选择排序(选择排序、堆排序),交换排序(冒泡排序、快速排序),归并排序
3.插入排序
3.1直接插入排序
原理:将整个区间分为有序区间和无序区间,每次选择无序区间的第一个元素,在有序区间的合适位置插入
3.2 插入排序的代码实现
//插入排序
public static void insertSort(int[] arr) {
int bound = 1;
for (; bound < arr.length; bound++) {
int v = arr[bound];
int cur = bound - 1;
for (; cur >= 0; cur--) {
if (arr[cur] > v) {
arr[cur + 1] = arr[cur];
}else{
break;
}
}
arr[cur + 1] = v;
}
}
3.3性能分析
时间复杂度为:最好为O(n)、平均为O(n2)、最坏为O(n2)
空间复杂度;O(1)
稳定性:稳定
4.希尔排序
4.1希尔排序原理
希尔排序法的基本思想是:先选定一个整数,把带排序文件中所有记录分成个gap组,所有距离为gap的记录分在同一个组内,并对每一组的记录进行排序。然后,取gap=gap/2,重复上述分组和排序工作。当gap=1时,所有记录在同一组内排好序。
1、希尔排序是对直接插入排序的优化
2、当gap>1时都是预排序,目的是让数组更接近于有序。当gap==1时,数组已经接近有序了。
4.2希尔排序的代码实现
public static void shellSort(int[] arr) {
int gap = arr.length / 2;
while (gap >= 1) {
_shellSort(arr, gap);
gap = gap / 2;
}
}
public static void _shellSort(int[] arr, int gap) {
int bound = gap;
for (; bound < arr.length; bound++) {
int v = arr[bound];
int cur=bound-gap;
for (; cur >= 0; cur--) {
if (arr[cur] > v) {
arr[cur + gap] = arr[cur];
}
else {
break;
}
}
arr[cur + gap] = v;
}
}
4.3性能分析
时间复杂度
最好:O(n),平均时间复杂度:O(n^1.3),空间复杂度:O(1)
稳定性;不稳定
5.选择排序
5.1 选择排序原理
每一次从无序区间选出最大(或最小)的一个元素,存放在无序区间的最后(或者最前),直到全部待排序的数据排完。
5.2 选择排序代码实现
public static void selectSort(int[] arr) {
int bound = 0;
for (; bound < arr.length; bound++) {
for (int cur = bound + 1; cur < arr.length; cur++) {
if (arr[cur] < arr[bound]) {
swap(arr, cur, bound);
}
}
}
}
public static void swap(int[] arr, int x, int y) {
int tem = arr[x];
arr[x] = arr[y];
arr[y] = tem;
5.3 性能分析
时间复杂度:O(n^2)
空间复杂度:O(1)
稳定性:不稳定
6.堆排序
6.1 堆排序原理
基本原理也是选择排序,只是不再使用遍历的方式查找无序区间的最大的数,而是通过堆来选择无序区间的最大数。
排升序建大堆,排降序建小堆。
6.2 堆排序代码实现
public static void heapSort(int[] arr) {
createHeap(arr);
int heapSize = arr.length;
for (int i = 0; i < arr.length; i++) {
swap(arr, 0, heapSize);
heapSize--;
shiftDown(arr, heapSize, 0);
}
}
public static void createHeap(int[] arr) {
for (int i = (arr.length - 1 - 1) / 2; i >= 0; i--) {
shiftDown(arr, arr.length, i);
}
}
public static void shiftDown(int[] arr, int size, int index) {
int parent = index;
int child = 2 * parent + 1;
while (child < size) {
if (child + 1 < size && arr[child + 1] > arr[child]) {
child = child + 1;
}
if (arr[parent] < arr[child]) {
swap(arr, parent, child);
}
else {
break;
}
parent=child;
child = 2 * parent + 1;
}
}
public static void swap(int[] arr, int x, int y) {
int tem = arr[x];
arr[x] = arr[y];
arr[y] = tem;
6.3性能分析
时间复杂度:O(n*log(n))
空间复杂度:O(1)
稳定性:不稳定
7.冒泡排序
7.1冒泡排序原理
在无序区间,通过相邻数的比较,将最大的数冒泡到无序区间的最后,持续整个过程,直到数组整体有序。
7.2冒泡排序代码实现
public static void bubbleSort(int[] arr) {
for (int bound = 0; bound < arr.length; bound++) {
for (int cur = arr.length - 1; cur >= bound; cur--) {
if (arr[cur - 1] > arr[cur]) {
swap(arr, cur - 1, cur);
}
}
}
}
public static void swap(int[] arr, int x, int y) {
int tem = arr[x];
arr[x] = arr[y];
arr[y] = tem;
7.3性能分析
最好时间复杂度:O(n),平均时间复杂度O(n2),最坏时间复杂度:O(n2).
空间复杂度:O(1)
稳定性:稳定
8.快速排序
8.1快速排序原理
1、从带排序区间选择一个数,作为基准值(pivot);
2、Partition:遍历整个待排序区间,将比基准值小的(可以包含相等的)放到基准值的左边,将播基准值大的放到基准值的右边;
8.2 快速排序代码实现
public static void quickSort(int[] arr) {
_quickSort(arr, 0, arr.length - 1);
}
public static void _quickSort(int[] arr, int left, int right) {
if (left >= right) {
return;
}
int index = partition(arr, left, right);
_quickSort(arr, left, index - 1);
_quickSort(arr, index + 1, right);
}
public static int partition(int[] arr,int left,int right) {
int v = arr[right];
int l = left;
int r = right;
while (l < r) {
while (l < r && arr[l] <= v) {
l++;
}
while (l < r && arr[r] >= v) {
r--;
}
swap(arr, l, r);
}
swap(arr, l, right);
return l;
}
8.3性能分析
时间复杂度
最好时间复杂度:O(nlog(n))
平均时间复杂度:O(nlog(n)
最坏时间复杂度:O(n^2)
空间复杂度
最好空间复杂度:O(nlog(n))
平均空间复杂度:O(nlog(n))
最坏时间复杂度:O(n)
9.归并排序
9.1归并排序原理
归并排序(MERGE-SORT)是建立在归并操作上的一种有效排序算法,该算法是采用分治法的一个非常典型应用。将已有序的子序合并,得到完全有序的序列;即先使每个子序有序,再使子序列断间有序。若将两个有序表合成一个有序表,称为二路归并。
9.2归并排序代码实现
public static void mergeSort(int[] arr) {
_mergeSort(arr, 0, arr.length);
}
// 辅助递归的方法
public static void _mergeSort(int[] arr, int left, int right) {
if (right - left <= 1) {
// 判定当前区间是不是只有一个元素或者没有元素
// 此时不需要进行排序
return;
}
int mid = (left + right) / 2;
// 先让 [left, mid) 区间变成有序
_mergeSort(arr, left, mid);
// 再让 [mid, right) 区间变成有序
_mergeSort(arr, mid, right);
// 合并两个有序区间
merge(arr, left, mid, right);
}