目录
排序的概念
插入排序
直接插入排序
希尔排序(缩小增量的排序)
选择排序
直接选择排序
堆排序
交换排序
冒泡排序
快速排序
归并排序
对于排序算法的总结
所有的排序算法均已上传至码云(Gitee)中,可以点击查看:七大经典排序算法代码
排序即使一串记录,按照其中某个或某些关键字的大小,递增或者递减的排列起来。排序分为内部排序和外部排序,内部排序即将数据元素全部放在内存中的排序,外部排序即当数据元素过多不能同时放在内存中排序时,根据排序的要求不能在内外存之间移动数据的排序.
在排序的算法中,经常讨论哪些算法是稳定的,哪些是不稳定的,那么如何判断排序算法是否稳定?
假定在待排序列中,存在多个相同的关键字记录,若经过排序,这些记录的相对次序未发生改变,则称该算法是稳定的,反之则为不稳定的.
例如在原序列中r[i] = r[j],且r[i]在r[j]之前,在经过排序后r[i]同样在r[j]之前,则该算法为稳定的排序算法.
在常见的七大排序算法中,冒泡排序、插入排序、归并排序是稳定的,选择排序、希尔排序、堆排序、快速排序是不稳定的.
直接插入排序:把待排序的记录按其关键码值的大小逐一插入到一个已排好序的有序序列中,直到所有的记录插入完为止,得到一个新的有序序列.
例如从小到大排序:
1.从第二位开始遍历,将前两个数据进行排序;
2.之后将第三个数据与已经排好序的前两个数据比较,将其放到合适的位置;
3.重复2步骤,将所有元素排列好为止.
/**
* 直接插入排序
* 时间复杂度:最好O(n) 最坏O(n^2)
* 空间复杂度:O(1)
* 稳定的排序算法
* @param array
*/
public static void insertSort(int[] array) {
for (int i = 1; i < array.length; i++) {
int tmp = array[i];
int j = i-1;
for(; j >= 0; j--) {
if(array[j] > tmp) {
array[j+1] = array[j];
}else {
break;
}
}
array[j+1] = tmp;
}
}
希尔排序是基于插入排序的思想的,将一组数据按照指定的距离分成若干个组,分别对其进行排序,当指定的距离变为1时,排序结束,具体如下所示
例如从小到大排序:
将现有的数据分成n/2个组,第一个数据和第n/2+1个数据为一对,之后依次对应,并对其进行排序,一次循环使所有的序列对排好顺序;然后将数据分为n/4个序列,继续对其进行排序,每次序列对数为上一次的1/2,当序列变为1个时,排序结束.
/**
* 希尔排序
* 时间复杂度:最好O(n) 最坏O(n^2)
* 空间复杂度:O(1)
* 不稳定的排序算法
* @param array
*/
public static void shellSort(int[] array) {
int gap = array.length;
while(gap > 1) {
gap /= 2;
shell(array,gap);
}
shell(array,1);
}
public static void shell(int[] array, int gap) {
for (int i = gap; i < array.length; i++) {
int tmp = array[i];
int j = i-gap;
for(; j >= 0; j-=gap) {
if(array[j] > tmp) {
array[j+gap] = array[j];
}else {
break;
}
}
array[j+gap] = tmp;
}
}
每一次从待排数据中选出最小(或最大)的一个元素,存放在序列的起始位置,直到所有待排序的元素排完.
算法描述(以从小到大排序为例)
1.在元素集合array[i] —— array[n-1]中选择关键码最小的数据元素;
2.若其不是这个数组中的第一个元素,则将这个元素与第一个元素交换;
3.在剩余的array[i+1]——array[n-1]集合中,重复1和2,直到集合剩余一个元素,此时已有序.
/**
* 选择排序
* 时间复杂度:O(n^2)
* 空间复杂度:O(1)
* 稳定性:不稳定
* @param array
*/
public static void selectSort(int[] array) {
for (int i = 0; i < array.length; i++) {
int minIndex = i;//记录最小值的下标
for (int j = i+1; j < array.length; j++) {
if(array[j] < array[minIndex]) {
minIndex = j;
}
}
swap(array,minIndex,i);
}
}
public static void swap(int[] array, int i, int j) {
int tmp = array[i];
array[i] = array[j];
array[j] = tmp;
}
堆排序是指利用堆这种数据结构所设计的一种排序算法,它是选择排序的一种,它是通过堆来选择数据的。当要排升序的时候建立大堆,排降序时建立小堆.
例如要排升序:
1.首先建立大根堆(若要排降序则建立小根堆)
2.将堆顶元素与堆最后一个元素进行交换,使末尾元素最大;
3.继续调整堆,再将堆顶元素与堆最后一个元素进行交换,得到第二大元素;
4.重复2和3,直至end == 0,排序结束.
/**
* 堆排序
* 时间复杂度:O(n*logn)
* 空间复杂度:O(1)
* 稳定性:不稳定
* @param array
*/
public static void heapSort(int[] array) {
//1、大根堆 O(N)
createHeap(array);
//2、排序O(n*logn)
int end = array.length-1;
while (end > 0) {
swap(array,0,end);
shiftDown(array,0,end);
end--;
}
}
//创建大根堆
private static void createHeap(int[] array) {
for (int parent = (array.length-1-1)/2; parent >= 0 ; parent--) {
shiftDown(array,parent,array.length);
}
}
//向下调整
private static void shiftDown(int[] array,int parent,int len) {
int child = (2*parent)+1;
while (child < len) {
if(child+1 < len && array[child] < array[child+1]) {
child++;//它一定保存的是左右孩子的最大值的下标
}
if(array[child] > array[parent]) {
swap(array,child,parent);
parent = child;
child = 2*parent+1;
}else {
break;
}
}
}
冒泡排序是一种简单的排序算法,它重复的遍历数组,每次比较两个元素,按照规定的排序规则将其排序,当数组已经有序时,排序结束.冒泡排序无论数组是否有序都会遍历一遍数组
/**
* 冒泡排序
* 时间复杂度:O(n^2)
* 空间复杂度:O(1)
* 稳定性:稳定
* @param array
*/
public static void bubbleSort(int[] array) {
for (int i = 0; i < array.length-1; i++) {
boolean flag = false;
for (int j = 0; j < array.length-1-i; j++) {
if(array[j] > array[j+1]) {
int tmp = array[j+1];
array[j+1] = array[j];
array[j] = tmp;
flag = true;
}
}
if(flag == false) {
break;
}
}
}
任取待排序元素序列中的某元素作为基准值,按照该排序码将待排序集合分割成两子序列,左子序列中所有元素均小于基准值,右子序列中所有元素均大于基准值,然后最左右子序列重复该过程,直至所有元素都排列在相应位置上为止.
/**
* 快速排序:
* 时间复杂度:
* 最好情况:O(n*logn) 可以每次 尽量将待排序序列 均匀的分割
* 最坏情况:O(n^2) 正序 逆序
* 空间复杂度:
* 最好情况:O(logn)
* 最坏情况:O(N)
* 稳定性:不稳定的排序
* @param array
*/
private static void quickSort(int[] array,int start,int end) {
if(start >= end) {
return;
}
int pivot = partition(array,start,end);
quickSort(array,start,pivot-1);
quickSort(array,pivot+1,end);
}
private static int partition(int[] array,int left,int right) {
int tmp = array[left];
while (left < right) {
while (left < right && array[right] >= tmp) {
right--;
}
//右边 找到小于tmp的数据
array[left] = array[right];
while (left < right && array[left] <= tmp) {
left++;
}
//右边 找到小于tmp的数据
array[right] = array[left];
}
array[left] = tmp;
return left;
}
归并排序是建立在归并操作上的一种有效的排序算法,该算法是采用分治法的一个经典应用.将已有序的子序列合并,得到完全有序的序列;先使每个子序列有序,再使子序列段间有序.
/**
* 归并排序
* 时间复杂度:n*logn 不管有序还是无序
* 空间复杂度:O(N)
* 稳定性:稳定
* @param array
*/
private static void mergeSort(int[] array,int low,int high) {
if(low >= high) {
return;
}
int mid = (low+high) >>> 1;
mergeSort(array,low,mid);
mergeSort(array,mid+1,high);
merge(array,low,high,mid);
}
//合并函数
private static void merge(int[] array,int low,int high,int mid) {
int[] tmp = new int[high-low+1];
int k = 0;
int s1 = low;
int e1 = mid;
int s2 = mid+1;
int e2 = high;
while (s1 <= e1 && s2 <= e2) {
//两个归并段 都有数据
if(array[s1] <= array[s2]) {
tmp[k++] = array[s1++];
}else {
tmp[k++] = array[s2++];
}
}
while (s1 <= e1) {
tmp[k++] = array[s1++];
}
while (s2 <= e2) {
tmp[k++] = array[s2++];
}
for (int i = 0; i < k; i++) {
array[i+low] = tmp[i];
}
}