排序算法,顾名思义,就是使一串记录,按照其中的某个或某些关键字的大小,递增或递减的排列起来的操作。
对于排序算法,主要分为两大类:内部排序和外部排序;
我们今天说的八大排序都是内部排序,内部排序又分为几大类,如下图所示:
对于一个排序算法的性能判断,主要有以下几个指标:
插入排序主要有两个排序算法,分别是:直接插入排序和希尔排序
1)基本思想: 将待插入的数据依次和前面已经排好序的序列中,从而有序的元素+1,依次向后遍历待插入元素。
2)步骤:
代码:
public 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;
}
}
3)时间复杂度:
O(N^2)
------>元素是逆序O(N)
------->前提是有序的根据时间复杂度可得出:元素越有序,排序速度越快
4)空间复杂度:O(1)
---->排序中没有用到辅助空间
5)稳定性: 是稳定的排序
array[j] > tmp
是才交换,并不会涉及到两个相等的元素交换位置,所以是稳定的;array[j] >= tmp
,那么此时的直接插入排序就是一个不稳定的排序。总结:
6)应用场景:
直接插入排序是比较耗费时间的操作,因为要搬移元素。所以如果序列是接近有序
或者数据个数比较少
比较适合插入排序
简单来说,希尔排序就是将一组元素分组后进行直接插入排序。
1)思想: 先通过分组,将序列变成接近有序,数据变少
所谓的接近有序就是:小的元素尽量靠前,大的元素尽量靠后,不大不小的元素靠中间。
那么希尔是如何分组的呢?
他每次将这组数分为m组,m一般为素数,分别对各组内进行直接插入排序,直到最后将整体看为1组进行直接插入排序。举个例子:
{ 9,1,2,5,7,4,8,6,3,5}
代码:
public void shellSort(int [] array)
{
int gap=array.length;
while(gap>1)
{
gap/=3;
for(int i=gap;i<array.length;i++)
{
int tmp=array[i];
for(int j=i-gap;j>=0;j-=gap)
{
if(array[j]>tmp)
{
array[j+gap]=array[j];
}else{
break;
}
}
array[j+gap]=tmp;
}
}
}
3)时间复杂度:O(N ^ 1.25)~O(1.6N^1.25) ---------->gap的取值不一样导致时间复杂度不一样
O(N^2)
------>元素是逆序O(N)
------->前提是有序的O(N^1.3)
4)空间复杂度:O(1)
---->排序中没有用到辅助空间
5)稳定性:不稳定的排序; 该排序算法在排序过程中是否将跨元素进行交换
6)使用场景:元素比较凌乱,个数比较多
选择排序主要有两个排序算法,分别是:选择排序和堆排序
1)思想: 每一次从无序区间选出最大(或最小)的一个元素,存放在无序区间的最后(或最前),直到全部待排序的数据元素排完。
2)步骤:
3)代码:
public void selectSort(int [] array)
{
for(int i=0;i<array.length;i++)
{
for(int j=i+1;j<array.length;j++)
{
if(array[i]>array[j])
{
int tmp=array[i];
array[i]=array[j];
array[j]=tmp;
}
}
}
}
4)时间复杂度: O(N^ 2) ----->论是有序还是无序都是O(N^2)
5) 空间复杂度:O(1)
6)稳定性:不稳定排序,发生跳跃式的交换
在上一篇博客中主要说了堆的概念和堆的基本操作,对于堆的两大应用,其中一个就是现在要说的堆排序。
什么是堆排序呢?就是借助大顶堆或者小顶堆来对一组数据进行排序。
1)基本原理: 也是选择排序,只是不在使用遍历的方式查找无序区间的最大的数,而是通过堆来选择无序区间的最大的数。
注意:排升序要建大堆;排降序要建小堆。
2)思路:
我们以升序为例,
建堆
:首先对这个数组中的元素建一个大堆,交换
:最后一个元素和堆顶元素进行交换(最大的元素就在最后)向下调整
:交换完为了保证是大顶堆,需要将堆顶元素向下调整3)代码:
public void heapSort(int [] array)
{
//创建一个大顶堆
creatHeap(array);
for(int i=array.length-1;i>=0;i--)
{
//交换
int tmp=array[i];
array[i]=array[0];
array[0]=tmp;
//向下调整
shiftDown(array,0,i);
}
}
//创建堆:时间复杂度: O(n)
public void createHeap(int [] array)
{
for(int i=(array.length-1-1)/2;i>=0;i--)
{
//向下调整
shiftDown(array,i,array.length);
}
}
//向下调整,时间复杂度:O(log2n)
public void shiftDown(int [] array,int root,int len)
{
int parent=root;
int child=parent*2+1;
while(child<=len)
{
if(child+1<len && array[child+1]<array[child])
{
child=chile+1;
}
if(array[child]>array[parent])
{
//交换
int tmp=array[child];
array[child]=array[parent];
array[parent]=tmp;
parent=child;
child=parent*2+1;
}else{
break;
}
}
}
4)时间复杂度(无论有序还是无序): O(n * log2n)
, 即:循环次数 * 向下调整的时间复杂度 + O(n) <建堆的时间复杂度> ------>把低次幂省略掉:O(n*log2n)
5)空间复杂度:O(1)
6)稳定性:不稳定
,因为有跳跃式的交换(第一次就把0号下标和9号下标进行交换)
评价:堆排序的时间复杂度是比较高的,唯一的缺点就是不稳定