排序的基本概念
- 稳定性:原数据中相等的两个元素次序在经过该排序算法之后次序不变,则称该算法是稳定的
- 排序算法的分类
1、插入类排序:在一个有序的列表中插入一个新的记录
例如:直接插入拍哦徐、这般插入排序、希尔排序
2、交换类排序:通过交换,让一个记录排在他最终的位置上
例如:冒泡排序、快速排序
3、选择类排序
例如:简单选择排序、堆排序
4、归并类排序
例如:二路归并排序
5、基数类排序
插入类排序
直接插入排序
直接插入排序执行流程
0:49(有序) 38 65 97 79 13 27 49*
1:38 49(有序) 65 97 79 13 27 49*
2:38 49 65(有序) 97 79 13 27 49*
...
算法实现
public int[] insertSort(int[] array){
for(int i=1;i<array.length;i++)//第0位独自作为有序数列,从第1位开始向后遍历
{
if(array[i]<array[i-1])//0~i-1位为有序,若第i位大于i-1位,继续寻位并插入,否则认为0~i位也是有序的,忽略此次循环,相当于continue
{
int temp=array[i];//保存第i位的值
int j;
for(j = i-1; j>=0 && temp<array[j];j--)//从第i-1位向前遍历并移位,直至找到小于第i位值停止
{
array[j+1]=array[j];
}
array[j+1]=temp;//插入第i位的值
}
}
算法性能
最坏情况下时间复杂度:这个序列都是逆序的情况,时间复杂度为O(n平方)
最好情况下时间复杂度:这个序列有序,O(n)
平均时间复杂度O(n平方)
稳定性:稳定
折半插入排序
前提:序列已经有序
执行流程:折半查找位置 + 插入
算法性能:与直接插入排序一样
希尔排序
1、概念:希尔排序又叫缩小增量排序,本质上还是一种插入排序。他首先不断通过缩小增量的方式将带排序的序列划分为几个子序列,对这些子序列进行直接插入排序,使这个序列整体基本有序,最后再进行一次直接插入排序
2、缩小增量的方式:以增量5分割序列、以增量3分割序列、以增量1分割序列(即直接插入排序)
3、执行流程
以增量5分割序列,得到如下子序列
原始序列:49 38 65 97 79 13 27 49* 55 04
子序列一:49 13
子序列二: 38 27
子序列三: 97 49*
子序列四: 79 04
则第一次希尔排序结果:13 27 49* 04 49 38 97 79
3、性能分析:平均时间复杂度为nlog2n(记住即可) + 不稳定
交换类排序
冒泡排序
1、算法介绍:冒泡排序使相邻元素间的比较和交换,每一次冒泡排序都会将最大的那个元素交换到最后
2、算法流程:
原序列:5 6 7 2 8 6 1
则一趟冒泡排序的执行流程为:5 6 2 7 6 1 8
3、算法实现
public void sort(int[] a) {
int temp = 0;
for (int i = a.length - 1; i > 0; --i) {
for (int j = 0; j < i; ++j) {
if (a[j + 1] < a[j]) {
temp = a[j];
a[j] = a[j + 1];
a[j + 1] = temp;
}
}
}
}
4、算法分析:
时间复杂度:
最坏情况下:逆序:O(n平方)
最好情况下:有序:O(n)
平均时间复杂度:O(n平方)
快速排序
1、算法介绍:快速排序将第一个元素作为枢纽值,其他值与这个枢纽值做对比,比它小的值放一边,比他大的值放另一边
2、执行流程
以第一个元素为枢纽
原始序列:
49(i) 38 65 97 79 13 27 49*(j)
②先从右边开始,找一个第一小于枢纽的值27,与枢纽值交换
27(i) 38 65 97 79 13 49(j) 49*
③再从左边开始,找一个第一个大于枢纽的值65,与枢纽值交换
27 38 49(i) 97 79 13 65(j) 49*
...
最终i == j时一趟快速排序结束
27 38 13 49(i)(j) 97 79 65 49*
最后再用递归对子序列进行快速排序
3、算法实现
public void sort(int arr[],int low,int high) {
int l=low;
int h=high;
int povit=arr[low];
while(l<h) {
while(l<h&&arr[h]>=povit) {
h--;
}
if(l<h){
int temp=arr[h];
arr[h]=arr[l];
arr[l]=temp;
l++;
}
while(l<h&&arr[l]<=povit) {
l++;
}
if(l<h) {
int temp=arr[h];
arr[h]=arr[l];
arr[l]=temp;
h--;
}
}
if(l>low)sort(arr,low,l-1);
if(h<high)sort(arr,l+1,high);
}
4、性能分析
时间复杂度:
最坏情况下时间复杂度:越接近有序,O(n的平方)
最好情况下时间复杂度:越无需,O(nlog2n)
平均:O(nlog2n)
递归—》压栈
选择类排序
简单选择排序
1、算法介绍,从头开始扫描。选择最小的元素与第一个元素做交换(要与直接插入排序作对比)
2、执行流程
原始序列:8 5 9 7 4 1 2 3
第一趟选择:1 8 5 9 7 4 2 3
第二趟排序:1 2 8 5 9 7 4 3
...
3、算法实现
void select_sort(int*a,int n)
{
register int i,j,min,t;
for(i=0;i<n-1;i++)
{
min=i;//查找最小值
for(j=i+1;j<n;j++)
if(a[min]>a[j])
min=j;//交换
if(min!=i)
{
t=a[min];
a[min]=a[i];
a[i]=t;
}
}
}
算法时间复杂度:都是O(n平方)
堆排序
1、算法介绍
可以把堆看成是完全二叉树的数据结构,且这颗二叉树满足:父结点的值大于(或小于)孩子结点的值,这样的堆称为大顶堆,因此可以将原始序列构造成一个大顶堆,堆的根结点即为最大的元素,输出后将剩下的元素继续构造成大顶堆,这样就能实现排序了。
2、执行流程
①将原始序列变为对应的二叉树
②从最后一个非叶结点开始,与孩子结点比较,调整成大顶堆
③输出根节点,让最后一个叶结点与根结点位置互换,将剩下的结点继续构造成大顶堆。(只有这个与根结点交换的叶结点不符合堆的要求,因此只要调整这个结点即可)
3、性能分析
时间复杂度都为O(nlog2n),
二路归并排序
1、算法介绍:元素两两归并
2、执行流程
2 5 9 7 6 4 3 8
二路归并排序:{2, 5}{7, 9}{4, 6}{3,8}
{2 5 7 9} {3 4 6 8}
3、性能分析
时间复杂度都为O(nlog2n)
基数排序
1、算法介绍:基数排序的思想是多关键字排序
2、算法流程:
原始序列:278 063 390 589 184 505 296 008 083
最低位先:390 063 303 184 505 196 278 008 589
十位次之,但是要在最低位先的基础上
303 505 008 063 278 184 589 390 196
百位最后,但是要在十位的基础上
008 063 187 196 178 303 390 505 589
性能分析:不重要