废话不多说,直接上代码
package club.javafan.blog;
import java.util.Arrays;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.stream.IntStream;
/**
* @author renjiahua3
* @date 2019/8/8
*/
public class SortTest {
/**
* 冒泡排序的流程:
* (1):和相邻元素进行比较,如果后个比前一个小(从小到大排序),则交换两个元素。
* (2):冒泡排序一趟之后,最大的元素已经位于数组末尾故第二趟不用比这个数。
* (3):但是会有个多余趟数的问题,如果数组已经有序,循环还在执行,故加了个标志,如果一趟没有进行交换,则停止循环。
* 时间复杂度:O(n^2) 稳定 稳不稳定看相同元素顺序发布发生变化
*
* @param array
*/
public static void bubbleSort(int[] array) {
if (array.length <= 0) {
return;
}
boolean change = false;
for (int i = 0; i < array.length; i++) {
for (int j = 1; j < array.length - i; j++) {
if (array[j - 1] > array[j]) {
int temp = array[j];
array[j] = array[j - 1];
array[j - 1] = temp;
change = true;
}
}
if (!change) {
break;
}
}
printArray.accept(array);
}
/**
* 快速排序流程:冒泡排序的改进
* 1.选择一个基准值 pivotKey默认取最低位或者最高位便于操作。
* 2.假定pivotKey是最小的值,从高位开始和基准值进行对比,找到第一个比pivotKey小的,array[low] = array[high]
* 3.覆盖完成后,开始从low 进行和pivotKey进行比较 找到第一个比pivotKey大的,array[high] = array[low]
* 4.覆盖完成之后继续从高位开始比较。直至 low == high
* 5.将pivotKey的值 覆盖到low == high 的位置。这个位置就是PivotKey的坑位。
* 6.将数组按照 pivotKey的位置左右进行划分
* 如序列:49, 38, 65, 97, 76, 13, 27, 49
* 划分过程为:每个分片为一个元素 有序
* [27, 38, 13] 49 [76, 97, 65, 49]
* [13] 27 [38]
* [49, 65] 76 [97]
* [] 49 [65]
* O(nlogn) 不稳定
*
* @param array
* @param low
* @param high
*/
public static void quickSort(int[] array, int low, int high) {
if (low < high) {
int pivotloc = partition(array, low, high);
System.out.print("划分为: " + Arrays.toString(Arrays.copyOfRange(array, low, pivotloc)));
System.err.print(" " + array[pivotloc] + " ");
System.out.print(Arrays.toString(Arrays.copyOfRange(array, pivotloc + 1, high + 1)) + "\n");
quickSort(array, low, pivotloc - 1);
quickSort(array, pivotloc + 1, high);
}
}
/**
* 快排的划分
*
* @param array
* @param low 传入下标
* @param high 传入下标
*/
public static int partition(int[] array, int low, int high) {
int pivotKey = array[low];
while (low < high) {
while (low < high && pivotKey <= array[high]) {
high--;
}
array[low] = array[high];
while (low < high && pivotKey >= array[low]) {
low++;
}
array[high] = array[low];
}
array[low] = pivotKey;
return low;
}
/**
* 选择排序流程:
* (1):假定数组的第i个元素为最小的,找到其余元素中最小的记录下标
* (2):找到最小的之后交换位置。
* (3):重复,直至结束
* 时间复杂度O(n^2) 不稳定
*
* @param array
*/
public static void selectSort(int[] array) {
if (array.length <= 1) {
return;
}
for (int i = 0; i < array.length; i++) {
int min = array[i];
int index = i;
for (int j = i + 1; j < array.length - 1; j++) {
if (min > array[j]) {
min = array[j];
index = j;
}
}
swap(array, i, index);
}
printArray.accept(array);
}
/**
* 堆排序算法流程:
* 0.将完全二叉树用数组表示,数组下标就是节点按广度优先遍历的序号
* 1.建立大顶堆或者小顶堆
* (1):一般从末尾的一个堆顶开始进行大顶堆创建
* (2):循环所有的堆顶节点,使堆顶元素均大于左右子节点
* (3):完成大顶堆的创建
* 如图:便是一个创建好的大堆
* 25
* / \
* 17 16
* / \ / \
* 7 3 11 12
* 2.排序
* (1):将堆顶元素和最后一个元素交换,元素个数减一。
* (2):执行建堆过程,依次类推使其有序。
* 时间复杂度O(nlogn) 不稳定
*
* @param arr
* @param count
*/
private static void heapSort(int[] arr, int count) {
/**
* 求最后节点的父节点 减一减二都一样
* 因为 left = 2 * i + 1 right = 2 * i + 2
*/
for (int i = (count - 2) / 2; i >= 0; i--) {
shiftDown(arr, count, i);
}
System.out.println("构建完成的大顶堆:" + Arrays.toString(arr));
/**
* 将大顶堆头结点(最大数)与最后一个元素交换位置,然后除去最后这个最大的元素,
* 再shiftDown操作维护该(除去最后一个元素的数组)堆为大顶堆。循环直到升序排序完成
*/
for (int j = count - 1; j > 0; j--) {
swap(arr, 0, j);
shiftDown(arr, j, 0);
System.out.println("去除最大的数,重建堆:" + Arrays.toString(arr));
}
}
/**
* 建立一个大顶堆的过程
* 1.判断当前节点是否是叶子节点
* 2.判断左节点和右节点哪个大
* 3.大的节点和父节点进行比较,如果比父节点小则进行交换
* 如下实例:
* 17 18
* / \ => / \
* 5 18 5 17
*
* @param arr 数组
* @param count 元素个数
* @param currentRoot 当前根节点的下标
*/
private static void shiftDown(int[] arr, int count, int currentRoot) {
while (2 * currentRoot + 1 < count) {
int max = 2 * currentRoot + 1;
if (max + 1 < count && arr[max + 1] > arr[max]) {
max += 1;
}
if (arr[currentRoot] >= arr[max]) {
break;
}
swap(arr, currentRoot, max);
currentRoot = max;
}
}
/**
* 2-路 归并排序流程:原理将长度为n的数组看为n个子序列 然后两两合并。如此重复直至序列有序。
* 1.将数组分成长度为2或者长度为1的序列
* 2.然后相邻两个序列比较谁小谁存入temp序列
* 3.直至归并完使数组有序
* 如: 初始:[49][38][65][97][76][13][27]
* 一趟归并:[38 49] [65 97] [13 76] [27]
* 二趟归并:[38 49 65 97] [13 27 76]
* 三趟归并 [13 27 38 49 65 76 97]
*
* @param arr 原数组
* @param tmp 临时数组
* @param low 下标开始位
* @param high 下标结束
*/
public static void mergeSort(int[] arr, int[] tmp, int low, int high) {
if (low < high) {
int mid = (low + high) / 2;
mergeSort(arr, tmp, low, mid);
mergeSort(arr, tmp, mid + 1, high);
merge(arr, low, mid, high, tmp);
}
}
/**
* 数组的归并
* 1.相邻数组进行比较:小的放入temp 下标++
* 2.如果一边有剩余则将剩余的全部放入temp数组中
* 3.将temp数组的数据考入进原数组的那段位置
* @param arr 数组
* @param low 低位
* @param mid 相邻数组的分界
* @param high 高位
* @param tmp 临时存放的数组
*/
public static void merge(int[] arr, int low, int mid, int high, int[] tmp) {
int i = 0;
int j = low, k = mid + 1;
while (j <= mid && k <= high) {
int i1 = arr[j] < arr[k] ? (tmp[i++] = arr[j++]) : (tmp[i++] = arr[k++]);
}
while (j <= mid || k <= high ) {
int i1 = j <= mid ? (tmp[i++] = arr[j++]) : (tmp[i++] = arr[k++]);
}
for (int t = 0; t < i; t++) {
arr[low + t] = tmp[t];
}
}
/**
* 插入排序本质是移动数组,找到合适的坑将自己栽入进去。
* 插入排序流程:
* 1.将第二个数开始设置哨兵
* 2.和前一个数比较 如果前面有数比后面的数大 则将那个数的数组后移将这个数插入那个空中
* 3.如果不大 则继续向前 直到结束
* 4.如果找到前面比后面大的,则赋值到后面。
* 5.继续设置哨兵
* 6.重复
* 原始数据 [49, 38, 65, 97, 76, 13, 27, 49]
* guard = 38 49>38 将数组的数向后移动直至小于38 然后将38 放进去
* 执行了一次[38,49,65, 97, 76, 13, 27, 49]
* guard = 65 不变 ,guard = 97 不变 guard = 76
* [38,49,65, 97, 76, 13, 27, 49] => [38,49,65,76,97,13, 27, 49]
* guard = 13 [13,38,49,65,76,97,27,49]
* guard = 27 [13,27,38,49,65,76,97,49]
* guard = 49 [13,27,38,49,49,65,76,97]
* @param array
*/
public static void insertSort(int[] array){
if (array.length <= 1){
return;
}
int guard;
for (int i = 1; i< array.length ;i++){
guard = array[i];
if (array[i] < array[i - 1]){
array[i] = array[i - 1];
int j;
for (j = i - 1;j>= 0 && array[j] > guard;j--){
array[j + 1] = array[j];
}
array[j + 1] = guard;
}
}
printArray.accept(array);
}
/**
* 希尔排序:直接插入排序的改进,又叫做缩小增量排序。
* 希尔排序流程:
* 1.确定增量步长的大小:(end - start)/2 。这里为什么除以二?
* 增量有好多种增量序列,不同的增量序列的时间复杂度也不一样。缩小增量取2 这种序列比较稳定
* 2.相邻序列的相同步长的元素进行比较
* 3.比较完成后缩小增量进行新的一轮 直至增量变为1 序列有序
* 时间复杂度 最好O(n^1.3) 平均O(n^1.5) 不稳定
* 原始:[49, 38, 65, 97, 76, 13, 27, 49]
* [ 49 13 27 49 76 38 65 97 ]
* [27 13 49 38 65 49 76 97 ]
* [13 27 38 49 49 65 76 97 ]
* @param arrays
*/
public static void shellSort(int[] arrays) {
for (int step = arrays.length / 2; step > 0; step /= 2) {
for (int i = step; i < arrays.length; i++) {
int j = i;
int temp = arrays[j];
while (j - step >= 0 && arrays[j - step] > temp) {
arrays[j] = arrays[j - step];
j = j - step;
}
arrays[j] = temp;
}
printArray.accept(arrays);
}
}
/**
* 数据交换
* @param arr 数组
* @param n 下标m
* @param m 下标n
* 注意:使用异或交换两个相同的数时 值为0 要避免这种情况。
*/
public static void swap(int[] arr, int n, int m) {
if (arr[m] == arr[n]) {
return;
}
arr[n] ^= arr[m];
arr[m] ^= arr[n];
arr[n] ^= arr[m];
}
/**
* 创建长度为length的int数组
*/
private static Function<Integer,int[]> createArray = length ->{
if (length > 0){
return new int[length];
}
return null;
} ;
/**
* 打印数组
*/
private static Consumer<int[]> printArray = array -> {
IntStream.of(array).mapToObj(item -> item + " ").forEach(System.out::print);
System.out.println();
};
public static void main(String[] args) {
int[] array = {49, 38, 65, 97, 76, 13, 27, 49};
// mergeSort(array,createArray.apply(array.length), 0, array.length - 1);
// bubbleSort(array);
// selectSort(array);
// quickSort(array, 0, array.length - 1);
// heapSort(array, array.length);
shellSort(array);
}
}
如果有差错,欢迎留言!!!
我的个人微博:不会敲代码的小白,欢迎关注!!!