文章目录
一、十种排序算法比较
排序算法可以分为内部排序和外部排序,内部排序是数据记录在内存中进行排序,而外部排序是因排序的数据很大,一次不能容纳全部的排序记录,在排序过程中需要访问外存。常见的内部排序算法有:插入排序、希尔排序、选择排序、冒泡排序、归并排序、快速排序、堆排序、基数排序等。以下为复杂度对比:
关于稳定性的解释:稳定性为排序后 2 个相等键值的顺序和排序之前它们的顺序相同
二、排序算法的选择
从上图可以明显的看出各种算法的优劣,在时间复杂度和空间复杂度两者之间很难有兼顾,所以只能根据需要选择。因此,不必记下所有算法,下面我用Java写了其中的四种常用的算法。
三、常用算法代码
0. 输入输出函数的封装
将一行数字读入,并按照空格分开,形成数组的方法抽象,方便其他所有排序算法的调用。同时,输出方法也抽象于此,以备实例化调用。
import java.util.Scanner;
/**
* @description: 提供输入和输出方法
* @author: 宇智波Akali
* @time: 2020/4/6 15:39
* @fromProject: 10_sort_Methods
* @Version: V1.0
*/
public class In_and_Out {
public int[] setList(){
/**
* @Description: 将序列格式化为数组
* @auther: 宇智波Akali
* @date: 15:43 2020/4/6
* @param: []
* @return: int[]
*/
System.out.println("请输出数组:");
Scanner scanner = new Scanner(System.in);
String input = scanner.nextLine(); //读入一行字符串
String[] str_line = input.split(" ");//将字符串按空格分隔成字符串数组
int[] list = new int[str_line.length];
for (int i=0;i<list.length;i++){
list[i] = Integer.parseInt(str_line[i]);
}
return list;
}
public void printList(int[] list){
/**
* @Description: 传入排好序的数组,执行打印
* @auther: 宇智波Akali
* @date: 15:52 2020/4/6
* @param: [list]
* @return: void
*/
for (Integer x: list){
System.out.print(x + " ");
}
}
}
1. 冒泡排序
算法步骤
-
比较相邻的元素。如果第一个比第二个大,就交换他们两个。
-
对每一对相邻元素作同样的工作,从开始第一对到结尾的最后一对。这步做完后,最后的元素会是最大的数。
-
针对所有的元素重复以上的步骤,除了最后一个。
-
持续每次对越来越少的元素重复上面的步骤,直到没有任何一对数字需要比较。
代码
public class Bubble_Sort_01 {
public static void main(String[] args) {
int [] list = new In_and_Out().setList(); //创建匿名对象调用In_and_Out中的setList方法
new In_and_Out().printList(bubble_Sort(list));//创建匿名对象调用其中的printLis方法
}
//****************************算法部分*********************************
public static int[] bubble_Sort(int[] list){
boolean trigger = true; //使用trigger判断原始序列是否已经有序
for (int i=list.length-1 ;i>=0;i--){
for (int j=0;j<i;j++){
if (list[j] > list[j+1]){ //将大于号改为小于则变成从大到小排序
int temp;
temp = list[j+1];
list[j+1] = list[j];
list[j] = temp;
trigger = false;
}
}
if (trigger) break;
}
return list;
}
}
2. 插入排序
算法步骤
-
将第一待排序序列第一个元素看做一个有序序列,把第二个元素到最后一个元素当成是未排序序列。
-
从头到尾依次扫描未排序序列,将扫描到的每个元素插入有序序列的适当位置。(如果待插入的元素与有序序列中的某个元素相等,则将待插入元素插入到相等元素的后面。)
代码
public class Bubble_Sort_01 {
public static void main(String[] args) {
int [] list = new In_and_Out().setList(); //创建匿名对象调用In_and_Out中的setList方法
new In_and_Out().printList(bubble_Sort(list));//创建匿名对象调用其中的printLis方法
}
//****************************算法部分*********************************
public static int[] bubble_Sort(int[] list){
boolean trigger = true; //使用trigger判断原始序列是否已经有序
for (int i=list.length-1 ;i>=0;i--){
for (int j=0;j<i;j++){
if (list[j] > list[j+1]){ //将大于号改为小于则变成从大到小排序
int temp;
temp = list[j+1];
list[j+1] = list[j];
list[j] = temp;
trigger = false;
}
}
if (trigger) break;
}
return list;
}
}
3. 归并排序
算法步骤
-
申请空间,使其大小为两个已经排序序列之和,该空间用来存放合并后的序列;
-
设定两个指针,最初位置分别为两个已经排序序列的起始位置;
-
比较两个指针所指向的元素,选择相对小的元素放入到合并空间,并移动指针到下一位置;
-
重复步骤 3 直到某一指针达到序列尾;
-
将另一序列剩下的所有元素直接复制到合并序列尾。
代码
import java.util.Arrays;
public class Merge_Sort_03 {
public static void main(String[] args) {
int[] list = new In_and_Out().setList();//读入并格式化List
new In_and_Out().printList(merge_sort(list));//打印
}
//********************算法部分*********************************
public static int[] merge_sort(int[] list){
if (list.length < 2){ //递归边界:数组长度小于2,将数组递归划分为单个元素
return list;
}
int middle = list.length/2;//定义中间下标
int[] L_list = Arrays.copyOfRange(list,0,middle); //定义左数组及内容范围
int[] R_list = Arrays.copyOfRange(list, middle,list.length);//定义右数组及内容范围
return merge(merge_sort(L_list), merge_sort(R_list)); //递归调用,进行归并排序
}
protected static int[] merge(int[] L, int[] R){
int[] result = new int[L.length+R.length];
int i = 0;
while (L.length>0 && R.length>0){
if (L[0] <= R[0]){ //小于改为大于则变成从大到小排序
result[i++] = L[0]; //左列的第一项小于右列第一项则向结果数组中填入L[0]
L = Arrays.copyOfRange(L,1,L.length);//将L的左边第一个去掉,之前的第二个变成第一个
}else {
result[i++] = R[0];
R = Arrays.copyOfRange(R,1,R.length);
}
//每次左or右列长度减小1,直到两个的长度其中有一个为0,即有一边数组全部加到result中
//这时候还剩一个数组,剩余部分必然都大于之前的数组,直接加入即可
}
while (L.length > 0) {
result[i++] = L[0];
L = Arrays.copyOfRange(L, 1, L.length);
}
while (R.length > 0) {
result[i++] = R[0];
R = Arrays.copyOfRange(R, 1, R.length);
}
return result;
}
}
4. 快速排序
算法步骤
-
从数列中挑出一个元素,称为 “基准”(pivot);
-
重新排序数列,所有元素比基准值小的摆放在基准前面,所有元素比基准值大的摆在基准的后面(相同的数可以到任一边)。在这个分区退出之后,该基准就处于数列的中间位置。这个称为分区(partition)操作;
-
递归地(recursive)把小于基准值元素的子数列和大于基准值元素的子数列排序;
代码
public class Quick_Sort_04 {
public static void main(String[] args) {
int[] list = new In_and_Out().setList(); //输入
new In_and_Out().printList(quick_sort(list)); //输出
}
//*************************算法部分**************************
private static int[] quick_sort(int[] list){
return sort(list, 0, list.length-1);
}
private static int[] sort(int[] list, int L, int R){
if (L < R){
int partitionIndex = partition(list, L, R); //接收一个已经确定了位置的数的下标,以此为分界线分块递归
sort(list, L, partitionIndex - 1);
sort(list, partitionIndex + 1, R);
}
return list;
}
private static int partition(int[] list, int L, int R){
int pivot_key = list[L];
while (L < R){
//一次外层循环只解决一次交换
while (L < R && list[R] >= pivot_key){
R--;
}
//将小于基准的放左边
list[L] = list[R];
while (L < R && list[L] <= pivot_key){
L++;
}
//将大于基准的放右边
list[R] = list[L];
}
list[L] = pivot_key; //当循环结束时,L=R,两者重合,将pivot_key的值放在此处,就确定了一个数的位置
//返回基准最后的位置
return L;
}
}