本文将利用代码尽可能详细的解释各种排序算法(桶排和各种排序算法的比较还没有总结完,后面会继续更新)

文章目录

  • ​​冒泡排序​​
  • ​​插入排序​​
  • ​​选择排序​​
  • ​​归并排序​​
  • ​​快速排序​​
  • ​​希尔排序​​

冒泡排序

/**
* 冒泡排序
* <p>
* 冒泡排序顾名思义就是整个过程像气泡一样上升,单向冒泡排序的基本思想是:
* 对于给定n个记录,从第一个记录开始依次对相邻的两个记录进行比较,当前面的记录大于后面的记录时,交换位置
* 进行一轮比较和交换后,n个记录中的最大记录将会位于第n位
* 然后对前(n-1)位进行比较,重复该过程直到记录只剩一个为止
*
* @author VicterTian
* @version V1.0
* @Date 2019/1/22
*/
public class BubbleSort {
public static void bubbleSort(int[] array) {
for (int i = 0; i < array.length - 1; i++) {
for (int j = array.length - 1; j > i; j--) {
if (array[j] < array[j - 1]) {
int temp = array[j];
array[j] = array[j - 1];
array[j - 1] = temp;
}
}
}
}

public static void main(String[] args) {
int[] array = {38, 65, 97, 76, 13, 27, 49};
bubbleSort(array);
System.out.println("Arrays.toString(array) = " + Arrays.toString(array));
}
}

插入排序

/**
* 插入排序
* <p>
* 对于给定的一组数据,初始时,假设第一个记录自成一格有序序列,其余记录为无需序列。
* 接着从第二个纪录开始,按照记录的大小,依次将当前处理的记录插入到其之前的有序序列中
* 直到插到最后一个元素为止
*
* @author VicterTian
* @version V1.0
* @Date 2019/1/22
*/
public class InsertSort {
public static void insertSort(int[] array) {
if (array == null) {
return;
}
for (int i = 1; i < array.length; i++) {
int temp = array[i];
int j = i;
if (array[i - 1] > temp) {
while (j >= 1 && array[j - 1] > temp) {
array[j] = array[j - 1];
j--;
}
}
array[j] = temp;
}
}

public static void main(String[] args) {
int[] array = {38, 65, 97, 76, 13, 27, 49};
insertSort(array);
System.out.println("Arrays.toString(array) = " + Arrays.toString(array));
}
}

选择排序

/**
* 选择排序
* <p>
* 选择排序是一种简单直观的排序算法,基本原理如下:
* 对于给定的一个数组,经过第一轮比较后得到的最小值与第一个记录的位置进行交换;
* 接着对不包括第一个记录以外的其他记录进行第二轮比较,得到的最小值与第二个记录进行交换
* 重复这个过程,直到进行比较的值只有一个为止
*
* @author VicterTian
* @version V1.0
* @Date 2019/1/22
*/
public class SelectionSort {
public static void selectSort(int[] array) {
int temp;
int flag;
int length = array.length;
for (int i = 0; i < length; i++) {
temp = array[i];
flag = i;
// 进行比较
for (int j = i + 1; j < length; j++) {
// 如果后面的值比第一个值大,就把那个值存在temp里,并且将其索引值记录在flag里
if (array[j] < temp) {
temp = array[j];
flag = j;
}
}
// 如果索引值不是开始比较的第一个元素,说明后面一定有比第一个元素小的值
if (flag != i) {
array[flag] = array[i];
array[i] = temp;
}
}
}

public static void main(String[] args) {
int[] array = {5,6,8,2,4,9,5,3};
selectSort(array);
System.out.println("Arrays.toString(array) = " + Arrays.toString(array));
}
}

归并排序

/**
* 归并排序
* <p>
* 归并排序是利用递归和分治技术将数据序列划分成为越来越小的半子表,再对半子表排序,
* 最后用递归方法将排序好的半子表合并成越来越大的有序数列。
* <p>
* 归并排序中,归是递归的意思,就是递归的将数组折半的分离为单个数组,
* 如[1,9,4,2]会被拆分为[1,9],[4,2]两个子数组,然后再分为[1],[9],[4],[2]
* <p>
* 归并排序中,并就是将分开的数据按从小到大或从大到小的顺序放在一个数组中,
* 如上面的数组合并为[1,2],[4,9],然后再合并为[1,2,4,9]
* <p>
* 归并排序的原理如下:
* 对于给定的一组记录,首先将每两个相邻的长度1的字序列进行归并,得到n/2(向上取整)个长度为2或1的有序子序列
* 再将其两两归并,反复执行此过程,直到得到一个有序序列
* <p>
* 所以,归并排序的关键就是两步:
* 第一步:划分半子表
* 第二步:合并半子表
* <p>
* 以[49,38,65,97,76,13,27]为例
* 初始关键字为:[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]
*
* @author VicterTian
* @version V1.0
* @Date 2019/1/22
*/
public class MargeSort {

public static void main(String[] args) {
int[] array = {38, 65, 97, 76, 13, 27, 49};
int p = 0;
int r = array.length - 1;
margeSort(array, p, r);
System.out.println("Arrays.toString(array) = " + Arrays.toString(array));
}

private static void margeSort(int[] array, int p, int r) {
if (p < r) {
int q = (p + r) / 2;
margeSort(array, p, q);
margeSort(array, q + 1, r);
merge(array, p, q, r);
}
}

private static void merge(int[] array, int p, int q, int r) {
int i, j, k, n1, n2;
n1 = q - p + 1;
n2 = r - q;
int[] leftArray = new int[n1];
int[] rightArray = new int[n2];
for (i = 0, k = p; i < n1; i++, k++) {
leftArray[i] = array[k];
}
for (i = 0, k = q + 1; i < n2; i++, k++) {
rightArray[i] = array[k];
}
for (k = p, i = 0, j = 0; i < n1 && j < n2; k++) {
if (leftArray[i] > rightArray[j]) {
array[k] = leftArray[i];
i++;
} else {
array[k] = rightArray[j];
j++;
}
}
if (i < n1) {
for (j = i; j < n1; j++, k++) {
array[k] = leftArray[j];
}
}
if (j < n2) {
for (i = j; i < n2; i++, k++) {
array[k] = rightArray[i];
}
}
}
}

快速排序

/**
* 快速排序
* <p>
* 快速排序是一种非常高效的排序算法,采用分而治之的思想,把大的拆分成小的,小的拆分成更小的。
* <p>
* 原理如下:
* 对于一组给定的记录,通过一趟排序后,将原序列划分成两部分,其中前一部分的所有记录值均比后一部分记录的小
* 然后在依次对前后两部分的记录进行排序,递归该过程,直到序列中的所有记录都有序为止
* <p>
* 快排的特点如下:
* 1. 最坏时间复杂度。最坏时间复杂度是指每次区间划分的结果都是基准关键字的左边(或者右边)序列为空,而另一区间中的记录项仅比排序前少了一项,即选择的基准关键字是带牌序列的最大值或者最小值
* 在这种情况下,需要进行(n-1)次区间划分。对于第K(0<k<n)次区间划分,划分前的序列长度为(n-k+1),需要进行(n-k)次记录的比较。因此,当k从1到n-1时,进行的比较次数总共为n(n-1)/2
* 所以,最坏情况下的时间复杂度为O(n^2)
* <p>
* 2. 最好时间复杂度。最好的情况是指每次区间的划分结果都是基准关键字左右两边的序列长度相等或相差为一,即选择的基准关键字为待排序的记录中的中间值,此时进行的比较次数为nlogn,
* 所以,最好的情况下,时间复杂度为O(nlogn)
* <p>
* 3.平均时间复杂度。快速排序的平均时间复杂度为O(nlogN)。虽然在最坏情况下的时间复杂度为O(n^2),但是在所有平均时间复杂度为O(nlogn)的算法中,快速排序的性能是最好的
* <p>
* 4.空间复杂度。快排的过程汇中需要一个栈空间来实现递归,当每次对区间的划分都比较均匀的情况下(最好的情况),递归树的最大深度为[logn]+1;
* 当每次区间划分都有一边为0时(最坏情况),递归树的最大深度为N
* 在每轮排序结束后比较基准关键字左右的记录数,对记录多的一边想进行排序,此时,栈的最大深度可降低为logn,因此,快速排序的平均时间复杂度为O(logn)
* <p>
* 5.基准关键字的选取。基准关键字的选取是决定快排算法性能的管件,常用基准关键字的选取非法有如下
* ① 三者区取中,三者是指在当前序列中,将其首尾,中间位置上的记录进行比较,选择三者的中值作为关键字,在划分开始前交换序列中的第一个记录与基准关键字的位置
* ② 取随机数,取left和right之间的一个随机数m用n[m]作为基准关键字
* <p>
* 快排与归并排序的原理都是基于分而治之思想,首先把元素分为两组,然后对这两组元素分别排序,最后把两组结合起来
* 不同点在于:他们进行的分组策略不一样,合并策略也不一样。
* 归并的分组策略为:假设待排序的元素存放在数组中,那么把数组前一半元素作为一组,把后一半元素作为一组。
* 快排的分组策略为:根据元素的值来分组,把大于某个值的作为一组,小于某个值的分为一组,该值称为基准
* 总的来说,这两种算法分组策略越简单,合并策略就越复杂,因为快排已经根据元素大小来分组了,所以合并时只需要把两个分组合并起来就可以
* 归并则需要对两个有序的数组根据大小合并
*
* @author VicterTian
* @version V1.0
* @Date 2019/1/22
*/
public class QuickSort {
public static void main(String[] args) {
int[] array = {38, 65, 97, 76, 13, 27, 49};
quickSort(array);
System.out.println("Arrays.toString(array) = " + Arrays.toString(array));
}

private static void quickSort(int[] array) {
sort(array, 0, array.length - 1);
}

private static void sort(int[] array, int low, int high) {
int i, j;
int index;
if (low >= high) {
return;
}
i = low;
j = high;
index = array[i];
while (i < j) {
// 当array[j] >= index时,不断进行j--操作,使array[j] < index
while (i < j && array[j] >= index) {
j--;
}
// 当array[j] < index时,将array[j]放入array[i]位置,并且放入array[i]指针前移(实现将比index值小的值放在index值前面)
if (i < j) {
array[i++] = array[j];
}
// 如果array[i]值比index值小,则i++ 继续找数组前面比index值大的数字
while (i < j && array[i] < index) {
i++;
}
// 将index前比index大的值放在index后面
if (i < j) {
array[j--] = array[i];
}
}
array[i] = index;
// 递归对index前的数据进行排序
sort(array, low, i - 1);
// 递归对index后的数据进行排序
sort(array, i + 1, high);
}
}

希尔排序

/**
* 希尔排序
* <p>
* 希尔排序也称为缩小增量排序,基本原理是:先将待排序的数组元素分成多个子序列,使得每个子序列的元素个数相对较少,然后对各个子序列分别进行直接插入排序
* 待整个待排序序列基本有序后,对所有元素进行一次直接插入排序
*
* @author VicterTian
* @version V1.0
* @Date 2019/1/22
*/
public class ShellSort {
public static void main(String[] args) {
int[] array = {38, 65, 97, 76, 13, 27, 49};
shellSort(array);
System.out.println("Arrays.toString(array) = " + Arrays.toString(array));
}

private static void shellSort(int[] array) {
int length = array.length;
int j;
for (int h = length / 2; h > 0; h = h / 2) {
for (int i = h; i < length; i++) {
int temp = array[i];
for (j = i - h; j >= 0; j = j - h) {
if (temp < array[j]) {
array[j + h] = array[j];
} else {
break;
}
}
array[j + h] = temp;
}
}
}
}