本篇博客给大家带来的是排序的知识点, 由于时间有限, 分两天来写, 上篇主要实现 前四种排序算法: 直接插入, 希尔, 选择, 堆排。
文章专栏: Java-数据结构
若有问题 评论区见
欢迎大家点赞 评论 收藏 分享
如果你不知道分享给谁,那就分享给薯条.
你们的支持是我不断创作的动力 .
目录
王子公主请阅
1.排序的概念及应用
1.1 排序的概念
排序:所谓排序,就是使一串记录按照其中的某个或某些关键字的大小,递增或递减的排列起来的操作。
1.3 常见的排序算法
2. 常见排序算法的实现(默认排升序)
2.1 插入排序
2.1.1基本思想:
直接插入排序是一种简单的插入排序法,其基本思想是:
如下模拟动图:
2.1.2 直接插入排序
直接插入排序具体操作:
第一步 理清思路:
当插入第i(i>=1)个元素时,前面的array[0],array[1],…,array[i-1]已经排好序,
1. 定义 tmp 保存 排序前 i 下标对应的值. for(i) 为 外循环, for(j) 为 内循环 , j = i -1 .
2. 遍历数组, 在内循环中, tmp 与 array[ j ] 进行比较,, 若是 tmp 小 则 [ j + 1] = [ j ];
若是 tmp 大 则 直接 break;
3. 在外循环中 将 最后 j + 1 位置的值 赋为 tmp值 , [ j + 1 ] = tmp;
第三步 画图理解 , 一目了然 :
第四步 代码实现:
/**
* 时间复杂度:
* 最好情况:数据完全有序的时候 1 2 3 4 5 :O(N)
* 最坏情况:数据完全逆序的时候 5 4 3 2 1 :O(N^2)
* 结论: 数据越有序,排序越快,
* 场景: 现在有一组基本有序的数据,那么用哪个排序好点? - 直接插入排序.
* 空间复杂度: O(1)
* 稳定性: 稳定的排序
* 一个本身就是稳定的排序 是可以实现为不稳定的排序的
* 相反 一个本身就不稳定的排序 是不可以实现为稳定的排序的.
*
* @param //直接插入排序
*/
public static 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 {
//array[j+1] = tmp; 执行了一次
break;
}
}
//当j=-1时,a[j+1]=tmp这条语句没被执行,所以再写一次
array[j+1] = tmp; //执行了两次, 故把第一次屏蔽.
}
}
最后总结:
2.1.3 希尔排序(缩小增量排序)
希尔排序本质上 就是 在对直接插入排序做优化。
第一步 了解基本思想:
/**
* 希尔排序
*
* 时间复杂度:
* n^1.3 - n^1.5
* 空间复杂度: O(1)
*
* 稳定性: 不稳定
* @param //shell
*/
public static void shellSort(int[] array) {
int gap = array.length;
while(gap > 1) {
gap /= 2;
shell(array,gap);
}
//gap /= 2;不用写 因为gap=2或3时, gap/2 = 1.已经进行了直接插入排序.
}
private static void shell(int[] array,int gap) {
for (int i = gap; i < array.length; i++) {
int tmp = array[i];
int j = i-gap;
for (; j >= 0; j -= gap) {
if(array[j] > tmp) {
array[j+gap] = array[j];
}else {
break;
}
}
array[j+gap] = tmp;
}
}
最后 希尔排序的特性总结:
2.2 选择排序
2.2.1基本思想:
/**
* 选择排序
*
* 时间复杂度:
* 最好: O(N^2)
* 最坏: O(N^2)
*空间复杂度:O(N)
*
* 稳定性: 不稳定
*
* @param array
*/
public static void selectSort(int[] array) {
for (int i = 0; i < array.length-1; i++) {
int minIndex = i;
for (int j = i+1; j < array.length; j++) {
if(array[j] < array[minIndex]) {
minIndex = j;
}
}
swap(array,minIndex,i);
}
}
public static void swap(int[] array,int i,int j) {
int tmp = array[i];
array[i] = array[j];
array[j] = tmp;
}
选择排序有 第二种写法, 反正都要遍历一遍数组, 不如把最大值和最小值都找出来, 最小的往最前放, 最大的往最后放.
第二种写法步骤:
1. 定义 left = 0, right = array.length-1, i = left + 1; minIndex = maxIndex = 0;
2. while( left < right )循环, i 在循环中, 遍历数组 找到最小元素下标 和 最大元素下标, 出循环 与 minIndex 和 maxIndex 交换.
3. 出循环后 考虑一个特殊情况 , 当 left = 0 下标 对应的值就是最大值时, 第二步出循环后的交换操作 将最大值交换到minIndex下标处了. 需要 再将 maxIndex 标记到最大值.
第二种写法代码实现:
public static void swap(int[] array,int i,int j) {
int tmp = array[i];
array[i] = array[j];
array[j] = tmp;
}
//选择排序的第二种写法.
public static void selectSort2(int[] array) {
int left = 0;
int right = array.length-1;
while(left < right) {
int minIndex = left;
int maxIndex = left;
for (int i = left; i <= right; i++) {
if(array[i] < array[minIndex]) {
minIndex = i;
}
if(array[i] > array[maxIndex]) {
maxIndex = i;
}
}
swap(array,left,minIndex);
//有一种情况是maxIndex原本就在left位置上,上面的交换将最大值交换到minIndex下标处了.导致后续的排序不正确.
if(maxIndex == left) {
maxIndex = minIndex;
}
swap(array,right,maxIndex);
left++;
right--;
}
}
第四步 直接选择排序的特性总结:
2.2.3 堆排序
/**
*堆排序
*
* 时间复杂度:O(N)+O(N*logN) 约等于 O(N*logN)
*
* 如果都以最坏的情况来看,堆排序是目前来说最快的,希尔排序其次.
* 假设希尔排序为O(N^1.3) 当N越大时,logN趋于不变所以,堆排序O(N*logN)要快一些.
*
* 空间复杂度:O(1)
* 稳定性:不稳定
*/
public static void heapSort(int[] array) {
//建大堆
createBigHeap(array);//O(N)
int end = array.length-1;
//O(N*logN)
while(end > 0) {
swap(array,0,end);
shiftDown(array,0,end);
end--;
}
}
private static void createBigHeap(int[] array) {
for (int parent = (array.length-1-1)/2; parent >= 0; parent--) {
shiftDown(array,parent,array.length);
}
}
private static void shiftDown(int[] array,int parent,int end) {
int child = (parent*2)+1;
while(child < end) {
//保证右子树存在并且当右子树大的时候,child++来到右子树的下标.
if(child+1 < end && array[child+1] > array[child]) {
child++;
}
if(array[child] > array[parent]) {
swap(array,child,parent);
parent = child;
child = parent*2+1;
}else {
//本身就是大根堆
break;
}
}
}
第三步 堆排序的特性总结: