七大排序应用场景
数据量规模较小,考虑插入或选择。当元素分布有序时插入将大大减少比较和移动记录的次数,如果不要求稳定性,可以使用选择,效率略高于插入;
数据量规模中等,使用希尔排序;
数据量规模较大,考虑堆排序(元素分布接近正序或逆序)、快速排序(元素分布随机)和归并排序(稳定性);
一般来说不使用冒泡。
性能总结
交换排序 :1、冒泡排序;2、快速排序
选择排序 :3、选择排序;4、堆排序
插入排序 :5、直接插入排序;6、希尔排序
归并排序:7、归并排序
1、冒泡排序
双指针
时间复杂度:O(N^2)
空间复杂度:O(1)
稳定性:稳定
java
//冒泡排序:时间复杂度O(n^2);空间复杂度O(1);稳定
public static void bubbleSort(int[] array){
for(int i=0;i<array.length-1;i++){//趟数
boolean sort = true;
for(int j=0;j<array.length-i-1;j++){
if(array[j] > array[j+1]){//交换,大值往后走
int tmp = array[j];
array[j] = array[j+1];
array[j+1] = tmp;
sort = false;//在交换证明无序
}
}
if(sort == true)//如果已经有序的话,直接返回
return;
}
}
2、快速排序
时间复杂度:最好O(NlgN),最坏O(N^2)
空间复杂度:O(lgN)
稳定性:不稳定
java
//快速排序:时间复杂度最好O(NlogN),最坏O(N^2);空间复杂度O(logN);不稳定
public static void quickSort(int[] array,int low,int high){
int start = low;
int end = high;
int key = array[start];//基准
while(start < end){
while(end>start && array[end]>=key){//从右边找小于基准key的值
end--;
}
if(array[end] < key){//小于基准的值放基准key左边
int tmp = array[end];
array[end] = array[start];
array[start] = tmp;
}
while(end>start && array[start]<key){//从左边找大于基准的值
start++;
}
if(array[start] > key){//大于放右边
int tmp = array[start];
array[start] = array[end];
array[end] = tmp;
}
}
if(start > low){//证明start往右边走了,low和start之间还存在无序,继续排序
quickSort(array,low,start-1);
}
if(high > end){//证明end往左走了,end和high之间无序,继续排序
quickSort(array,end+1,high);
}
}
3、选择排序
时间复杂度:O(N^2)
空间复杂度:O(1)
稳定性:不稳定
java
//选择排序:时间复杂度O(n^2);空间复杂度:O(1);不稳定
public static void selectSort(int[] array){
int index = 0;
for(int i=0;i<array.length;i++){//趟数
int tmpMin = array[i];//每趟默认当前位置值在剩下的所有值中最小
for(int j=i;j<array.length;j++){//在剩下位置中找到最小值
if(array[j]<=tmpMin){
tmpMin = array[j];
index = j;
}
}
array[index] = array[i];//交换最小值和i位置的值
array[i] = tmpMin;
}
}
4、堆排序
时间复杂度:O(N*lgN)
空间复杂度:O(1)
稳定性:不稳定
堆排序步骤分三步 :
1、对初始数组构造大根堆;
2、将堆顶元素交换到最后;
3、对剩余数组构造大根堆
java
public static void heapSort(int[] arr) {
//构造大根堆
heapInsert(arr);
int size = arr.length;
while (size > 1) {
//固定最大值
swap(arr, 0, size - 1);
size--;
//构造大根堆
heapify(arr, 0, size);
}
}
//构造大根堆(通过新插入的数上升)
public static void heapInsert(int[] arr) {
for (int i = 0; i < arr.length; i++) {
//当前插入的索引
int currentIndex = i;
//父结点索引
int fatherIndex = (currentIndex - 1) / 2;
//如果当前插入的值大于其父结点的值,则交换值,并且将索引指向父结点
//然后继续和上面的父结点值比较,直到不大于父结点,则退出循环
while (arr[currentIndex] > arr[fatherIndex]) {
//交换当前结点与父结点的值
swap(arr, currentIndex, fatherIndex);
//将当前索引指向父索引
currentIndex = fatherIndex;
//重新计算当前索引的父索引
fatherIndex = (currentIndex - 1) / 2;
}
}
}
//将剩余的数构造成大根堆(通过顶端的数下降)
public static void heapify(int[] arr, int index, int size) {//index-父节点的索引,size-剩余数组大小
int left = 2 * index + 1;
int right = 2 * index + 2;
while (left < size) {
int largestIndex;
//判断孩子中较大的值的索引(要确保右孩子在size范围之内)
if (arr[left] < arr[right] && right < size) {
largestIndex = right;
} else {
largestIndex = left;
}
//比较父结点的值与孩子中较大的值,并确定最大值的索引
if (arr[index] > arr[largestIndex]) {
largestIndex = index;
}
//如果父结点索引是最大值的索引,那已经是大根堆了,则退出循环
if (index == largestIndex) {
break;
}
//父结点不是最大值,与孩子中较大的值交换
swap(arr, largestIndex, index);
//将索引指向孩子中较大的值的索引
index = largestIndex;
//重新计算交换之后的孩子的索引
left = 2 * index + 1;
right = 2 * index + 2;
}
}
//交换数组中两个元素的值
public static void swap(int[] arr, int i, int j) {
int temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}
5、直接插入排序
时间复杂度:最坏情况下O(N^2),最好情况下O(N),(只用i动,j不用走)
空间复杂度:O(1)
稳定性:稳定
直接插入排序步骤分三步 :
1、循环选择无序区间的第一个值作为要插入的值
2、将要插入的值往前比较,直到前一个数比插入的值小
3、插入
java
//直接插入排序
public static int[] insertsort(int[] arr){
for(int i=1;i<arr.length;i++){
int insertVal = arr[i];//保存要插入的值
int index = i-1;//准备往前比较
while(index>=0&&arr[index]>=insertVal){//当插入的值比被插入的值小
arr[index+1] = arr[index];//则将arr[index]向后移动
index--;//index向前移动
}
//找到插入的值的位置,插入
arr[index+1] = insertVal;
}
return arr;
}
6、希尔排序(插入排序的一种)
时间复杂度:O(NlgN)~O(N^2)
空间复杂度:O(1)
稳定性:不稳定
希尔排序是对直接插入排序的优化,和gap的取值有关
java
//希尔排序
public void shellSort(int[] array){
int gap = 3;
while(gap>0){
for(int i=gap;i<array.length;i++){
int insertVal = array[i];
int index = i-gap;
while(index>=0 && array[index]>insertVal){
array[index+gap] = array[index];
index -= gap;
}
array[index+gap] = insertVal;
}
gap--;
}
}
7、归并排序
时间复杂度:O(NlgN)
空间复杂度:O(N)
稳定性:稳定
java
//归并排序
public static int[] mergesort(int[] data){
sort(data,0,data.length-1);
return data;
}
public static void sort(int[] data,int left,int right){
if(left >= right){
return;
}
int center = (left+right)/2;
sort(data,left,center);//递归左边数组
sort(data,center+1,right);//递归右边数组
merge(data,left,center,right);
}
public static void merge(int[] data,int left,int center,int right){
int[] tmpArr = new int[data.length];//临时数组
int mid = center+1;//右边数组的第一个索引
int third = left;//临时数组的索引
int tmp = left;//左边数组的第一个索引
while(left <= center&&mid <= right){//比较左右数组元素的大小,小的放入临时数组
if(data[left]<=data[mid]){
tmpArr[third++] = data[left++];
}else{
tmpArr[third++] = data[mid++];
}
}
//将剩下的一个数组的剩余部分放入临时数组
while(mid<=right){
tmpArr[third++] = data[mid++];
}
while(left<=center){
tmpArr[third++] = data[left++];
}
//拷贝回原数组
while(tmp <= right){
data[tmp] = tmpArr[tmp++];
}
}