目录
1..什么是排序
排序是按照某种顺序(升序或降序)排列序列元素的一种算法,排序的输出是输入的排列或重新排列。
2.为什么要进行排序
排序可以显著降低问题的复杂度,可以使用排序作为减少查找复杂度的一种技术。
3.排序的分类
-
比较的次数
基于比较的次数对排序算法进行分类,对于基于比较的排序算法,最好的情况下时间复杂度为O(n㏒n),而在最坏的情况下则为O(n²)。
- 交换的次数
- 内存的使用
有些排序算法是原地的,不需要额外的内存空间,仅需要O(1)或O(㏒ n)的内存开销用于创建临时排序数据的辅助存储位置。
- 递归
排序算法是可以递归的(比如快速排序)或者非递归(如选择排序),也有同时采用递归和非递归的排序算法。
- 稳定性
- 适应性
少数的排序算法的复杂度依赖于序列的初始排列情况(如快速排序),输入的初始排列将会影响算法的运行时间。
5.常用的简单排序
*以下的排序流程图摘自于https://www.cnblogs.com/skywang12345/
1.冒泡排序
冒泡排序是一种最简单的排序算法,与其他排序算法的唯一显著的优势,它可以检测输入序列是否已经是排序的。
基本思想是迭代地对输入序列中的一个元素到最后一个元素进行两两比较,当其顺序为反序的时候,就将这两个元素的位置交换。直到没有反序的记录为止。
排序过程:
代码实现:
public static void bubbleSort(int[] arr) {
for(int i = arr.length-1;i>0;i--) {
for (int j = 0; j < i; j++) {
if(arr[j] > arr[j+1]) {
int temp = arr[j];
arr[j] = arr[j+1];
arr[j+1] = temp;
}
}
}
}
性能:
最坏情况下时间复杂度:O(n²)
平均情况下空间复杂度:O(n²)
最好情况下空间复杂度:O(1)
2.快速排序
快速排序又叫分区交换排序,是对冒泡排序的一种改进。改进的着眼点是:在冒泡排序中,记录的比较和移动都是咋相邻位置进行的,记录每次交换后只能后移一个位置,因而总的比较次数和移动此时比较多。在快速排序中,记录的比较是从两端向中间进行的,关键码较大的记录一次就能从前面移动到后面,关键码较小的记录一次就能从后面移动到前面,记录的移动间隔较大,减少了移动的次数。
基本思想:首先选择一个轴值,将待排序序列分为独立的两个部分,左侧记录均小于等于轴值,右侧记录均大于等于轴值,依次重复进行递归,直到整个序列有序为止。
排序过程:
算法代码实现:
/**
* 快速排序算法
* 第一次可以选择第一个或者中间一个值作为轴值,
* 然后将其换到第一个位置
* @param args
*/
public static void quickSort(int[] arr,int first,int end) {
if(first<end) {
int pivot = partition(arr, first, end);
System.out.println(pivot);
quickSort(arr, first, pivot-1);
quickSort(arr, pivot+1, end);
}
}
/**
* 找到中值
* @param args
*/
public static int partition(int[] arr, int first,int end) {
while(first<end) {
while(first<end&&arr[first]<=arr[end]) {
end--;
}
if(first<end) {
int temp = arr[end];
arr[end] = arr[first];
arr[first] = temp;
first++;
}
while(first<end&&arr[first]<=arr[end]) {
first++;
}
if(first<end) {
int temp = arr[end];
arr[end] = arr[first];
arr[first] = temp;
end--;
}
}
return first;//返回中间值的位置
}
性能:
最好情况下时间复杂度:O(nlogn)
最坏情况下时间复杂度:O(n)
平均情况下时间复杂度:O(nlogn)
最坏情况下空间复杂度:O(1) 辅助
3.直接插入排序
直接插入排序是插入排序中最简单的排序方法,
基本思想:依次将待排序序列中的每一个记录插入到已排好的序列中,直到所有记录都排好序
排序过程:
代码实现:
/**
* 直接插入排序就是将待排序的序列中的每一个元素插入到
* 已经排好的序列中
* @param arr
*/
public static void sInsertSort(int[] arr) {
//从第二个元素开始,与前面的元素比较并将其插入到合适的位置
for (int i = 1; i < arr.length; i++) {
int temp = arr[i];
int j = i-1;
while(j>=0) {
if(temp<arr[j]) {
arr[j+1] = arr[j];
j--;
}else{
break;
}
}
arr[j+1] = temp;
}
}
性能:
最好情况下时间复杂度:O(n²)
最坏情况下时间复杂度:O(n²)
平均情况下时间复杂度:O(n²)
最坏情况下空间复杂度:O(n²) 辅助
4.希尔排序
希尔排序又称为缩小增量排序,是一个泛化的插入排序,是对直接插入排序的拓展,两者主要的差异在于直接插入排序中的比较是在两个相邻元素之间进行的,而希尔排序利用可变增量使算法进行到最后一步才比较相邻的元素,其具有交换相距较远的记录的能力。
主要思想:先将整个待排序序列分割成若干个子序列,然后在每个子序列内部分别进行直接插入排序,比如比较和交换数组中每个距离为h的元素,然后逐步缩小增量,继续进行直接插入排序,一旦h变为1且是h间距排序的,则数组排序完成。
排序过程:
代码实现:
/**
* 希尔排序,将待排序序列分割成若干个子序列,
* 然后分别对若干个子序列进行直接插入排序,
* 然后将分割的距离缩小,再进行直接插入排序,
* 依次执行,直至分割距离为1
*/
public static void shellSort(int[] arr) {
int d = arr.length/2; //设置初始分割距离
for(int i = d;i >= 1;i=i/2) {
for(int j = i;j<arr.length;j++) {
int temp = arr[j];
int k = j-i;
while(k>=0) {
if(temp<arr[k]) {
arr[k+i] = arr[k];
k-=i;
}else {
break;
}
}
arr[k+i] = temp;
}
}
}
性能:
最好情况下时间复杂度取决于间隔序列,最好情况下:O(nlog²n)
最坏情况下时间复杂度:O(n)
平均情况下时间复杂度:取决于间隔序列
最坏情况下空间复杂度:O(n)
5.简单选择排序
简单选择排序是选择排序算法中最简单的一种排序算法(原地排序算法)。适用于小文件。由于选择操作是基于键值的且交换操作只是在需要时才进行,所以选择排序常用于数值较大和键值较小的文件。
基本思想:第i趟排序在待排序序列arr[i]~arr[n](1<=i<=n-1)中选取关键码最小的记录,并和第i个记录进行交换作为有序序列第i个记录。
排序过程:
代码实现:
/**
*
* 简单选择排序
* 每趟排序将无序中的最小的元素选取出来,
* 且每次排序只移动一次元素
*
*/
public static void simSelectSort(int[] arr) {
for (int i = 0; i < arr.length; i++) {
int index = i;
for (int j = i+1; j < arr.length; j++) {
if(arr[j]<=arr[index]) {
index = j;
}
}
int temp = arr[index];
arr[index] = arr[i];
arr[i] = temp;
}
}
性能:
最好情况下时间复杂度:O(n²)
最坏情况下时间复杂度:O(n)
平均情况下时间复杂度:O(n²)
最坏情况下空间复杂度:O(1) 辅助