1.关于选择排序
概念(Selection sort)
选择排序:是指第一次从待排序的数据元素中选出最小(或最大)的一个元素,存放在序列的起始位置,然后再从剩余的未排序元素中寻找到最小(大)元素,然后放到已排序的序列的末尾。以此类推,直到全部待排序的数据元素的个数为零。
平均时间复杂度:
n
2
n^2
n2;
最坏时间复杂度:
n
2
n^2
n2;
最好时间复杂度:
n
2
n^2
n2;
空间复杂度:1;
稳定性:最不稳定;
每次选择最小的元素位置,放在起始位置
public static void main(String[] args) {
//生成一个随机整数数组
Random random = new Random();
int [] arr = new int[10];
for (int m =0;m <10;m++){
int anInt = random.nextInt(100);
arr[m] = anInt;
}
//复制数组
for (int i=0;i<arr.length;i++)
brr[i] = arr[i];
//打印排序前的数组
System.out.println(Arrays.toString(arr));
System.out.println(Arrays.toString(brr));
//选择排序,遍历数组,找到最小数的坐标,然后交换位置
for(int i = 0;i<arr.length-1;i++){
int minpos = i;
for (int j = i+1;j<arr.length;j++)
//每一次遍历,找到最小元素的坐标
minpos = arr[j]<arr[minpos]?j:minpos;
//然后交换最小元素坐标
int temp = arr[i];
arr[i] = arr[minpos];
arr[minpos] = temp;
}
//输出排序后的数组,与排序前数组对比
System.out.println("....."+Arrays.toString(arr));
System.out.println("...........");
//与工具类排序对比
Arrays.sort(brr);
System.out.println("工具类排序:" +Arrays.toString(brr))
}
运算结果:
每次选择最大的元素位置,放在末尾位置
public static void main(String[] args) {
//生成一个随机整数数组
Random random = new Random();
int [] arr = new int[10];
int [] brr = new int[10];
for (int m =0;m <10;m++){
int anInt = random.nextInt(100);
arr[m] = anInt;
}
//复制数组
for (int i=0;i<arr.length;i++)
brr[i] = arr[i];
System.out.println(Arrays.toString(arr));
System.out.println(Arrays.toString(brr));
//选择排序,遍历数组,找到最大数的坐标,然后交换位置
for(int i = 0;i<arr.length-1;i++){
int maxpos = 0;
for (int j = 0;j<arr.length-i;j++)
maxpos = arr[j]>arr[maxpos]?j:maxpos;
//交换最大元素的位置
int te = arr[arr.length-1-i];
arr[arr.length-1-i] = arr[maxpos];
arr[maxpos] = te;
}
System.out.println("选择排序:"+Arrays.toString(arr));
System.out.println("...........");
//与工具类排序对比
Arrays.sort(brr);
System.out.println("工具类排序:" +Arrays.toString(brr));
}
运算结果:
2.关于冒泡排序
概念(Bubble sort)
冒泡排序:是指它重复地走访过要排序的元素列,依次比较两个相邻的元素,如果第一个比第二个大,就交换他们两个。对每一对相邻元素做同样的工作,从开始第一对到结尾的最后一对。在这一点,最后的元素应该会是最大的数。持续每次对越来越少的元素重复上面的步骤,直到没有任何一对数字需要比较,最后就实现了排序的功能。
平均时间复杂度:
n
2
n^2
n2;
最坏时间复杂度:
n
2
n^2
n2;
最好时间复杂度:n;
空间复杂度:1;
稳定性:稳定;
代码实现
package com;
import java.util.Arrays;
import java.util.Random;
public class bubbleSort {
public static void main(String[] args) {
Random random = new Random();
int [] arr = new int[10000];
int [] brr = new int[10000];
for (int m =0;m <10000;m++){
int anInt = random.nextInt(100000);
arr[m] = anInt;
}
//复制数组
for (int i=0;i<arr.length;i++)
brr[i] = arr[i];
System.out.println(Arrays.toString(arr));
System.out.println(Arrays.toString(brr));
long startTime = System.currentTimeMillis();
BubbleMethod(arr,brr);
long endTiem = System.currentTimeMillis();
System.out.println("用时:" + (endTiem-startTime));
}
private static void BubbleMethod(int[] arr, int[] brr) {
for (int i =0 ;i < arr.length-1; i++){
for (int j = 0; j< arr.length-1-i;j++){
if (arr[j] > arr[j+1]) {
int temp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = temp;
}
}
}
System.out.println("Bubble排序:"+Arrays.toString(arr));
System.out.println("...........");
Arrays.sort(brr);
System.out.println("工具类排序:" +Arrays.toString(brr));
}
}
运算结果
3.关于插入排序
概念
插入排序(Insertionsort),它的基本思想是将一个元素插入到已经排好序的有序表中,从而一个新的、元素数增1的有序表。在其实现过程使用双层循环,外层循环对除了第一个元素之外的所有元素,内层循环对当前元素前面有序表进行待插入位置查找,并进行移动。插入排序在样本小且基本有序的时候效率比较高。
平均时间复杂度:
n
2
n^2
n2;
最坏时间复杂度:
n
2
n^2
n2;
最好时间复杂度:n;
空间复杂度:1;
稳定性:稳定;
代码实现
package com;
import java.util.Arrays;
import java.util.Random;
/**
* @author shixiaodong
* @creat 2021-06-01
*/
public class insertionSort {
public static void main(String[] args) {
//生成一个随机整数数组
Random random = new Random();
int[] arr = new int[10000];
int[] brr = new int[10000];
for (int m = 0; m < 10000; m++) {
int anInt = random.nextInt(100000);
arr[m] = anInt;
}
//复制数组
for (int i = 0; i < arr.length; i++)
brr[i] = arr[i];
System.out.println(Arrays.toString(arr));
System.out.println(Arrays.toString(brr));
long startTime = System.currentTimeMillis();
shellMethod(arr, brr);
long endTiem = System.currentTimeMillis();
System.out.println("用时:" + (endTiem - startTime));
}
private static void shellMethod(int[] arr, int[] brr) {
//次外层循环,控制间隔与间隔之间交换
for (int i = 1; i < arr.length; i++) {
//内层循环,控制间隔内交换
for (int j = i; j > 0 && arr[j] < arr[j - 1]; j--) {
//交换
int temp = arr[j];
arr[j] = arr[j - 1];
arr[j - 1] = temp;
}
}
System.out.println("Insertion排序:" + Arrays.toString(arr));
System.out.println("...........");
Arrays.sort(brr);
System.out.println("工具类排序:" + Arrays.toString(brr));
}
}
运行结果
4.希尔排序
概念
希尔排序(Shellsort),也叫缩减增量排序比普通插入排序效率高,但是不稳定。希尔排序是把记录按下标的一定增量分组,对每组使用直接插入排序算法排序;随着增量逐渐减少,每组包含的关键词越来越多,当增量减至 1 时,整个文件恰被分成一组,算法便终止。所以最后的增量一定要是1。
平均时间复杂度:
n
1
.
3
n^1.3
n1.3;
最坏时间复杂度:
n
2
n^2
n2;
最好时间复杂度:n;
空间复杂度:1;
稳定性:不稳定;
代码实现
package com;
import java.util.Arrays;
import java.util.Random;
public class shellSort {
public static void main(String[] args) {
//生成一个随机整数数组
Random random = new Random();
int [] arr = new int[10000];
int [] brr = new int[10000];
for (int m =0;m <10000;m++){
int anInt = random.nextInt(100000);
arr[m] = anInt;
}
//复制数组
for (int i=0;i<arr.length;i++)
brr[i] = arr[i];
System.out.println(Arrays.toString(arr));
System.out.println(Arrays.toString(brr));
long startTime = System.currentTimeMillis();
shellMethod(arr,brr);
long endTiem = System.currentTimeMillis();
System.out.println("用时:" + (endTiem-startTime));
}
private static void shellMethod(int[] arr,int[] brr) {
//knuth 序列,效率最高
int h =1;
while(h<=arr.length/3) h=h*3+1;
//最外层循环,控制间隔
for(int gap = h;gap>0;gap=(gap-1)/3){
//次外层循环,控制间隔与间隔之间交换
for(int i=gap;i<arr.length;i++){
//内层循环,控制间隔内交换
for(int j=i;j>gap-1 && arr[j]<arr[j-gap];j -= gap){
//交换
int temp = arr[j];
arr[j] = arr[j-gap];
arr[j-gap] = temp;
}
}
}
System.out.println("shell排序:"+Arrays.toString(arr));
System.out.println("...........");
Arrays.sort(brr);
System.out.println("工具类排序:" +Arrays.toString(brr));
}
}
运行结果
插入排序和希尔排序的区别
- 希尔排序是在插入排序基础上的升级,效率比较高;
- 在稳定性方面,插入排序稳定,希尔排序不稳定;
- 希尔排序相比于插入排序在代码上多一层循环,这多出来的一层循环是用来控制插入间隔的逐渐缩小。
5.归并排序
概念
归并排序,是创建在归并操作上的一种有效的排序算法。算法是采用分治法(Divide and Conquer)的一个非常典型的应用,且各层分治递归可以同时进行。归并排序思路简单,速度仅次于快速排序,为稳定排序算法,一般用于对总体无序,但是各子项相对有序的数列。
归并排序是用分治思想,分治模式在每一层递归上有三个步骤:
1.分解(Divide):将n个元素分成个含n/2个元素的子序列。
2.解决(Conquer):用合并排序法对两个子序列递归的排序。
3.合并(Combine):合并两个已排序的子序列已得到排序结果。
平均时间复杂度:
n
l
o
g
2
N
nlog_2{N}
nlog2N;
最坏时间复杂度:
n
l
o
g
2
N
nlog_2{N}
nlog2N;
最好时间复杂度:
n
l
o
g
2
N
nlog_2{N}
nlog2N;
空间复杂度:1;
稳定性:稳定;
代码实现
package com;
import com.sun.scenario.effect.Merge;
import java.util.Arrays;
import java.util.Random;
public class mergeSort {
public static void main(String[] args) {
Random random = new Random();
int [] arr = new int[10000];
int [] brr = new int[10000];
for (int m =0;m <10000;m++){
int anInt = random.nextInt(100000);
arr[m] = anInt;
}
//复制数组
for (int i=0;i<arr.length;i++)brr[i] = arr[i];
System.out.println(Arrays.toString(arr));
System.out.println(Arrays.toString(brr));
long startTime = System.currentTimeMillis();
mergeSort(arr,brr);
long endTiem = System.currentTimeMillis();
System.out.println("用时:" + (endTiem-startTime));
}
private static void mergeSort(int[] arr, int[] brr) {
Sort(arr,0,arr.length-1);
System.out.println("merge排序:"+Arrays.toString(arr));
System.out.println("...........");
Arrays.sort(brr);
System.out.println("工具类排序:" +Arrays.toString(brr));
}
/**
* 将数组通过递归一直二分,然后将最小二分的数组排好序后合并
*/
private static void Sort(int[] arr, int left, int right) {
if (left == right) return;
int mid = left +(right-left)/2;
Sort(arr,left,mid);
Sort(arr,mid+1,right);
merge(arr,left,mid,right);
}
private static void merge(int[] arr, int left, int mid, int right) {
//创建一个长度和arr相同的空数组
int[] temp = new int[right-left+1];
int i = left;
int j = mid+1;
int k = 0;
//将两个二分数组从做小坐标开始比较,将最小值一次交给temp数组
while (i<=mid && j<=right) temp[k++]=arr[i]<arr[j]?arr[i++]:arr[j++];
while (i<=mid) temp[k++]=arr[i++];
while (j<=right) temp[k++]=arr[j++];
//将排好序的temp交给arr数组
for (int m=0;m<temp.length;m++) arr[left+m]=temp[m];
}
}
运行结果
6.快速排序
概念
快速排序,通过一趟排序将要排序的数据分割成独立的两部分,其中一部分的所有数据都比另外一部分的所有数据都要小,然后再按此方法对这两部分数据分别进行快速排序,整个排序过程可以递归进行,以此达到整个数据变成有序序列。
快速排序算法通过多次比较和交换来实现:
- 首先设定一个分界值,通过该分界值将数组分成左右两部分。
- 将大于或等于分界值的数据集中到数组右边,小于分界值的数据集中到数组的左边。此时,左边部分中各元素都小于或等于分界值,而右边部分中各元素都大于或等于分界值。
- 然后,左边和右边的数据可以独立排序。对于左侧的数组数据,又可以取一个分界值,将该部分数据分成左右两部分,同样在左边放置较小值,右边放置较大值。右侧的数组数据也可以做类似处理。
- 重复上述过程,可以看出,这是一个递归定义。通过递归将左侧部分排好序后,再递归排好右侧部分的顺序。当左、右两个部分各数据排序完成后,整个数组的排序也就完成了。
平均时间复杂度:
n
l
o
g
2
N
nlog_2{N}
nlog2N;
最坏时间复杂度:
n
2
n^2
n2;
最好时间复杂度:
n
l
o
g
2
N
nlog_2{N}
nlog2N;
空间复杂度:
l
o
g
2
N
log_2{N}
log2N;
稳定性:不稳定;
代码实现
package com;
import java.util.Arrays;
import java.util.Random;
public class quickSort {
public static void main(String[] args) {
Random random = new Random();
int[] arr = new int[10000];
int[] brr = new int[10000];
//生成随机数组
for (int m = 0; m < 10000; m++) {
int anInt = random.nextInt(100000);
arr[m] = anInt;
}
//复制数组
for (int i = 0; i < arr.length; i++)
brr[i] = arr[i];
System.out.println(Arrays.toString(arr));
System.out.println(Arrays.toString(brr));
long startTime = System.currentTimeMillis();
//实现排序
quickMethod(arr, brr);
long endTiem = System.currentTimeMillis();
System.out.println("用时:" + (endTiem - startTime));
}
private static void quickMethod(int[] arr, int[] brr) {
Sort(arr, 0, arr.length - 1);
//与工具类方法对比
System.out.println("quick排序:" + Arrays.toString(arr));
System.out.println("...........");
Arrays.sort(brr);
System.out.println("工具类排序:" + Arrays.toString(brr));
}
private static void Sort(int[] arr, int left, int right) {
if (left >= right) return;
int mid = partition(arr, left, right);
//方法递归
Sort(arr, left, mid - 1);
Sort(arr, mid + 1, right);
}
private static int partition(int[] arr, int left, int right) {
//选择数组最有边界的值作为轴
int pivot = arr[right];
int i = left;
int j = right - 1;
while (i <= j) {
while (i <= j && arr[i] <= pivot) i++;
while (i <= j && arr[j] > pivot) j--;
if (i < j) Swap(arr, i, j);
}
Swap(arr, i, right);
return i;
}
/**元素交换*/
private static void Swap(int[] arr, int i, int j) {
int temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}
}
运行结果
7.计数排序
概念
计数排序,其思想是对于给定的输入序列中的每一个元素x,确定该序列中值小于x的元素的个数(此处并非比较各元素的大小,而是通过对元素值的计数和计数值的累加来确定)。一旦有了这个信息,就可以将x直接存放到最终的输出序列的正确位置上。
计数排序是一个非基于比较的排序算法,它的优势在于在对一定范围内的整数排序时,快于任何比较排序算法,适合量大但是范围小的情况,对源数有要求。
平均时间复杂度:
n
+
k
n+k
n+k;
最坏时间复杂度:
n
+
k
n+k
n+k;
最好时间复杂度:
n
+
k
n+k
n+k;
空间复杂度:
n
+
k
n+k
n+k;
稳定性:稳定;
代码实现
package com;
import java.util.Arrays;
import java.util.Random;
/**
* @author shixiaodong
* @creat 2021-06-09
*/
public class countSort {
public static void main(String[] args) {
//生成一个随机整数数组
Random random = new Random();
int[] arr = new int[1000];
int[] brr = new int[1000];
for (int m = 0; m < 1000; m++) {
int anInt = random.nextInt(100);
arr[m] = anInt;
}
//复制数组
for (int i = 0; i < arr.length; i++) brr[i] = arr[i];
System.out.println(Arrays.toString(arr));
System.out.println(Arrays.toString(brr));
long startTime = System.currentTimeMillis();
countMethod(arr, brr);
long endTiem = System.currentTimeMillis();
System.out.println("用时:" + (endTiem - startTime));
}
private static void countMethod(int[] arr, int[] brr) {
int[] result = Sort(arr);
System.out.println("计数排序:" + Arrays.toString(result));
System.out.println("...........");
Arrays.sort(brr);
System.out.println("工具类排序:" + Arrays.toString(brr));
}
private static int[] Sort(int[] arr) {
int[] result = new int[arr.length];
//count数组的个数与arr数组元素的取值范围相同,这里取值范围从为[0,10)
int[] count = new int[100];
for (int i=0;i<arr.length;i++){
count[arr[i]]++;
}
//累加数组
for (int i = 1;i<count.length;i++){
count[i]=count[i]+count[i-1];
}
//从后往前遍历,相同元素的顺序没有改变:不稳定 -> 稳定
for (int i=arr.length-1;i>=0;i--){
result[--count[arr[i]]]=arr[i];
}
return result;
}
}
运行结果
参考:B站马士兵