排序算法衡量指标
关于排序算法的重要性我就不啰嗦了,不重要你也遇不到这篇文章。安利一个学习算法免费看动画的网站,该文的动图都来自这个网站 https://visualgo.net/zh/sorting ,感谢站长。
那么多的经典和野鸡排序算法,讲之前我们先关注一下排序算法的衡量指标:
十大经典排序算法
排序方式In-Place指的是原地排序,Out-place指的非原地排序
看了江山图之后,我们先来看江山图里混成了最底层的弟弟们,冒泡排序,选择排序和插入排序,这几个是时间复杂度最高的排序。
这个排序不简单,大学里面每个学校都必教的一个排序
给定一个N个元素的数组,冒泡法排序将:
算法思想
一次冒泡会让至少一个元素移动到它应该在的位置,重复n次,就完成了n个数据的排序工作。
交换旗帜变量 = 假 (False)
从 i = 1 到 最后一个没有排序过元素的指数
如果 左边元素 > 右边元素
交换(左边元素,右边元素)
交换旗帜变量 = 真(True)
while 交换旗帜变量
动图演示
冒泡排序动画
public class BubbleSort {
public static int[] bubbleSort(int[] arr) {
if (arr == null || arr.length < 2) {
return arr;
}
int n = arr.length;
// 第一层循环,每循环一次,排好一个元素,在最右边
for (int i = 0; i < n; i++) {
// 第二层循环,到右边第一个没有排序过元素地方结束,进行比较交换
for (int j = 0; j < n -i - 1; j++) {
// 比较,大于小于号决定是按照从大到小排还是从小到大排
if (arr[j + 1] < arr[j]) {
// 交换
int t = arr[j];
arr[j] = arr[j+1];
arr[j+1] = t;
}
}
}
return arr;
}
)
可知第二层循环是进行比较交换
的核心逻辑,第一层循环用来确定排好了几个元素,决定了第二层循环的比较次数,n-i-1之所以减去一,是因为比如剩下10个元素没有排序,10个元素只有9对,需要比较九次。
稳定性分析
稳定。只有交换才可以改变两个元素的前后顺序,当有相邻的两个元素大小相等的时候,我们不做交换,相同大小的数据在排序前后不会改变顺序,所以冒泡排序是稳定的排序算法。如果代码交换逻辑改成
if (arr[j + 1] <= arr[j]),加了个=,那么就不稳定了。
时间复杂度分析
算法描述
给定 N 个项目和 L = 0 的数组,选择排序将:
算法思想
选择排序分已排序区间和未排序区间,其实就是从头遍历,要排第几个元素,每次从剩余未排序元素里面找最小的元素,交换位置。
重复(元素个数-1)次
把第一个没有排序过的元素设置为最小值
遍历每个没有排序过的元素
如果元素 < 现在的最小值
将此元素设置成为新的最小值
将最小值和第一个没有排序过的位置交换
动图演示
选择排序
红色表示当前遍历到的最小值。其他三个颜色和上面一样。
public class SelectSort {
public static int[] selectSort(int[] a) {
int n = a.length;
// 从头开始遍历,选定某个元素待排序
for (int i = 0; i < n - 1; i++) {
// 将这个元素设为最小值
int min = i;
// 遍历所有未排过序的元素,从第i个元素右边开始
for (int j = i + 1; j < n; j++) {
// 如果元素小于当前最小值,那么最小值改为当前值
if(a[min] > a[j])
min = j;
}
// 以上得到了当前最小值,将当前最小值提到前面
//交换
int temp = a[i];
a[i] = a[j];
a[j] = temp;
}
return a;
}
}
算法描述
插入排序类似于大多数人安排扑克牌的方式。
插扑克牌之插入排序
算法思想
插入排序和选择排序一样,都分已排序区和位排序区。
将第一个元素标记为已排序
遍历每个没有排序过的元素
“提取” 元素 X
i = 最后排序过元素的指数 到 0 的遍历
如果现在排序过的元素 > 提取的元素
将排序过的元素向右移一格
否则:插入提取的元素
动图演示
插入排序
红色代表选中的需要排序的。
代码实现
public class InsertSort {
public static int[] insertSort(int[] arr) {
if(arr == null || arr.length < 2)
return arr;
int n = arr.length;
// 下标为0的数默认是有序的,从下标为1的数开始遍历,将其放入他该去的地方
for (int i = 1; i < n; i++) {
// 申请一个变量记录要插入的数据,也就是动图中红色的元素
int tmp = arr[i];
// 从已经排序的序列最右边的开始比较,找到比其小的数,即动图中绿色的元素序号
int j = i;
// 红色元素下标大于0,要插入元素与遍历到的元素满足大小关系,遍历到的元素往后挪位腾位置 // 继续遍历,直到不满足大小关系停止,这个地方就是它的位置
// 即红色元素小于绿色元素时,绿色元素挪位
while (j > 0 && tmp < arr[j - 1]) {
// 绿色元素往后挪一位
arr[j] = arr[j - 1];
j--;
}
// 存在比其小的数,插入
if (j != i) {
arr[j] = tmp;
}
}
return arr;
}
}
稳定性分析
稳定。在插入排序中,对于值相同的元素,我们可以选择将后面出现的元素,插入到前面出现元素的后面,这样就可以保持原有的前后顺序不变,所以插入排序是稳定的排序算法。
时间复杂度分析
如果要排序的数据已经是有序的,我们并不需要搬移任何数据。如果我们从尾到头在有序数据组里面查找插入位置,每次只需要比较一个数据就能确定插入的位置。所以这种情况下,最好是时间复杂度为O(n)。注意,这里是从尾到头遍历已经有序的数据。
如果数组是倒序的,每次插入都相当于在数组的第一个位置插入新的数据,所以需要移动大量的数据,所以最坏情况时间复杂度为O(n2)。
下一篇写希尔,归并,快排和堆排序,还是按照这种格式,有收获的三连走起,欢迎关注我,我是小魔女阿甘,扫码有惊喜哦。