Java–排序算法
(1)计数排序
用额外数组存储各个待排序列数出现的次数
import java.util.Scanner;
public class Main
{
public static void main(String[] args)
{
Scanner sca = new Scanner(System.in);
int n = sca.nextInt();//输入n个整数
int [] arr = new int [n];
arr[0] = sca.nextInt();
int max = arr[0];
for (int i = 1; i < n; i++)
{
arr[i] = sca.nextInt();//把输入的数依次输入到数组中
if (max < arr[i])//找出最大数
{
max = arr[i];
}
}
int [] book = new int [max + 1];//开max+1的额外数组
for (int i = 0; i < n; i++)//标记出现次数
{
book[arr[i]]++;
}
for (int i = 0; i <= max; i++)//循环book数组
{
for (int j = 1; j <= book[i]; j++)
{
System.out.print(i);
}
}
sca.close();
}
}
排序算法 | 平均时间复杂度 | 最好情况 | 最坏情况 | 空间复杂度 | 稳定性 |
---|---|---|---|---|---|
计数排序 | O(n + k) | O(n + k) | O(n + k) | O(k) | 稳定 |
PS: k为桶的个数
需要额外数组空间,利用空间换时间
(2)基数排序
第一趟依次根据待排序数的个位数排序,第二趟依次根据待排序数的十位数排序,……
import java.util.Arrays;
import java.util.Scanner;
public class Main
{
public static void main(String[] args)
{
Scanner sca = new Scanner(System.in);
int n = sca.nextInt();//n个待排序数
int [] arr = new int [n];
for (int i = 0; i < n; i++)//随机生成1000以内n个数
{
arr[i] = (int)(Math.random() * 1000);
}
radixSort(arr);//基数排序
System.out.println(Arrays.toString(arr));//输出
sca.close();
}
/**
* 基数排序
* @param arr 待排序数组
*/
public static void radixSort(int [] arr)
{
int len = arr.length;//待排序数组长度
int [][] book = new int [10][len];//开十个桶,每个桶最大容量是待排序数组的容量
int [] index = new int [10];//桶的索引
int max = arr[0];
for (int i = 1; i < len; i++)//求出最大值
{
if (max < arr[i])
{
max = arr[i];
}
}
int maxSize = (max + "").length();//根据最大值得到要循环到几位数
for (int i = 0, m = 1; i < maxSize; i++, m *= 10)
{
for (int j = 0; j < len; j++)//利用桶排序,看图解
{
int t = arr[j] / m % 10;
book[t][index[t]] = arr[j];
index[t]++;
}
int arr_index = 0;
for (int j = 0; j < 10; j++)//更新arr数组
{
for (int k = 0; k < index[j]; k++)
{
arr[arr_index] = book[j][k];
arr_index++;
}
index[j] = 0;
}
}
}
}
排序算法 | 平均时间复杂度 | 最好情况 | 最坏情况 | 空间复杂度 | 稳定性 |
---|---|---|---|---|---|
基数排序 | O(n * k) | O(n * k) | O(n * k) | O(n + k) | 稳定 |
PS: k为桶的个数
需要额外数组空间,利用空间换时间
(3)冒泡排序
n - 1趟循环相邻两两比较交换,第 i 趟实现第 i(或 n - i + 1)位数完成排序
import java.util.Arrays;
import java.util.Scanner;
public class Main
{
public static void main(String[] args)
{
Scanner sca = new Scanner(System.in);
int n = sca.nextInt();//n个待排序数
int [] arr = new int [n];
for (int i = 0; i < n; i++)//随机生成1000以内n个数
{
arr[i] = (int)(Math.random() * 1000);
}
bubbleSort(arr, n);//冒泡排序
System.out.println(Arrays.toString(arr));//输出
sca.close();
}
/**
* 冒泡排序
* @param arr 待排序数组
* @param n 待排序数组长度
*/
public static void bubbleSort(int [] arr, int n)
{
boolean flag = false;//优化
for (int i = 0; i < n - 1; i++)//走n - 1趟即可
{
for (int j = 0; j < n - i - 1; j++)
{
if (arr[j] > arr[j + 1])//两两比较交换
{
flag = true;//说明该趟排序有交换
int temp = arr[j + 1];
arr[j + 1] = arr[j];
arr[j] = temp;
}
}
if (!flag)//如果某趟排序没有交换说明数组已经排好序
{
return;
}
else
{
flag = false;//重置
}
}
}
}
排序算法 | 平均时间复杂度 | 最好情况 | 最坏情况 | 空间复杂度 | 稳定性 |
---|---|---|---|---|---|
冒泡排序 | O(n^2) | O(n) | O(n^2) | O(1) | 稳定 |
PS: 待排序列越趋向有序,冒泡排序的时间复杂度越接近O(n)
(4)快速排序
找一个基准(随意一个数),把大于基准的数放在基准的右边,把小于基准的数放在小于基准的左边,基准的左边以此类推,基准的右边也以此类推
import java.util.Arrays;
import java.util.Scanner;
public class Main
{
public static void main(String[] args)
{
Scanner sca = new Scanner(System.in);
int n = sca.nextInt();
int [] arr = new int [n];
for (int i = 0; i < n; i++)//随机生成10以内n个数
{
arr[i] = (int)(Math.random() * 10);
}
quickSort(arr, 0, n - 1);//快排
System.out.println(Arrays.toString(arr));//输出
sca.close();
}
/**
* 快速排序
* @param arr 待排序数组
* @param left 数组最左边
* @param right 数组最右边
*/
public static void quickSort(int [] arr, int left, int right)
{
int mid = arr[(left + right) / 2];//找中间数作为基准
int l = left;
int r = right;
while (l < r)//l >= r时跳出循环
{
while (arr[l] < mid)//直到找到大于等于基准的数
{
l++;
}
while (arr[r] > mid)//直到找到小于等于基准的数
{
r--;
}
if (l >= r)//l >= r时跳出循环
{
break;
}
int temp = arr[l];//交换
arr[l] = arr[r];
arr[r] = temp;
if (arr[l] == arr[r])//当遇到arr[l] = arr[r] = 基准数时会陷入循环
{
l++;//l++或者r--都可
}
}
if (left < r - 1)//左边递归
{
quickSort(arr, left, r - 1);
}
if (l + 1 < right)//右边递归
{
quickSort(arr, l + 1, right);
}
}
}
排序算法 | 平均时间复杂度 | 最好情况 | 最坏情况 | 空间复杂度 | 稳定性 |
---|---|---|---|---|---|
快速排序 | O(n log n) | O(n log n) | O(n^2) | O(log n) | 不稳定 |
PS:空间主要是递归时消耗
(5)选择排序
在arr[0]arr[n]寻找最小值与arr[0]交换,在arr[1]arr[n]寻找最小值与arr[1]交换,……
import java.util.Arrays;
import java.util.Scanner;
public class Main
{
public static void main(String[] args)
{
Scanner sca = new Scanner(System.in);
int n = sca.nextInt();//n个数
int [] arr = new int [n];
for (int i = 0; i < n; i++)//随机生成1000以内n个数
{
arr[i] = (int)(Math.random() * 1000);
}
selectSort(arr, n);//调用选择排序
System.out.println(Arrays.toString(arr));//输出
sca.close();
}
/**
* 选择排序
* @param arr 待排序数组
* @param n 待排序数组长度
*/
public static void selectSort(int [] arr, int n)
{
int min, minIndex;
for (int i = 0; i < n - 1; i++)//从最小位开始排序
{
min = arr[i];
minIndex = i;
for (int j = i + 1; j < n; j++)//从第i+1位到第n位中找比第i位还小的值
{
if (min > arr[j])//更新
{
min = arr[j];
minIndex = j;
}
}
arr[minIndex] = arr[i];
arr[i] = min;//第i位完成排序
}
}
}
排序算法 | 平均时间复杂度 | 最好情况 | 最坏情况 | 空间复杂度 | 稳定性 |
---|---|---|---|---|---|
选择排序 | O(n^2) | O(n^2) | O(n^2) | O(1) | 不稳定 |
(6)插入排序
将待排序数组中的数依次取出插入到有序的数组中
import java.util.Arrays;
import java.util.Scanner;
public class Main
{
public static void main(String[] args)
{
Scanner sca = new Scanner(System.in);
int n = sca.nextInt();
int [] arr = new int [n];
for (int i = 0; i < n; i++)//随机生成n个1000以内的数
{
arr[i] = (int)(Math.random() * 1000);
}
insertSort(arr, n);//插入排序
System.out.println(Arrays.toString(arr));//输出
sca.close();
}
/**
* 插入排序
* @param arr 待排序数组
* @param n 待排序数组长度
*/
public static void insertSort(int [] arr, int n)
{
int t, value;
for (int i = 1; i < n; i++)//把arr[0]看作已排序序列,arr[1]~arr[n]看作未排序序列
{
t = i - 1;
value = arr[i];
while (t >= 0 && value < arr[t])//从已排序后面开始比较,得到插入的位置
{
arr[t + 1] = arr[t];
t--;
}
if (t + 1 != i)//插入
{
arr[t + 1] = value;
}
}
}
}
排序算法 | 平均时间复杂度 | 最好情况 | 最坏情况 | 空间复杂度 | 稳定性 |
---|---|---|---|---|---|
插入排序 | O(n^2) | O(n) | O(n^2) | O(1) | 稳定 |
(7)希尔排序
先分 len(数组长度)/2 组进行插入排序,再分 len(数组长度)/2/2 组进行插入排序,直到整体进行插入排序,主要解决插入排序碰到最坏情况,减少后移的次数提供速度
import java.util.Arrays;
import java.util.Scanner;
public class Main
{
public static void main(String[] args)
{
Scanner sca = new Scanner(System.in);
int n = sca.nextInt();
int [] arr = new int [n];
for (int i = 0; i < n; i++)//随机生成n个1000以内的数
{
arr[i] = (int)(Math.random() * 1000);
}
shellSort(arr);//希尔排序
System.out.println(Arrays.toString(arr));//输出
sca.close();
}
/**
* 希尔排序
* @param arr 待排序数组
*/
public static void shellSort(int [] arr)
{
int len = arr.length;//数组长度
for (int i = len / 2; i >= 1; i /= 2)//把数组分为i组
{
for (int j = i; j < len; j++)//从第i位开始向后扫描
{
int k = j;
int temp = arr[k];
while (k - i >= 0 && temp < arr[k - i])//插入排序原理
{
arr[k] = arr[k - i];
k -= i;
}
if (temp < arr[k])
{
arr[k] = temp;
}
}
}
}
}
排序算法 | 平均时间复杂度 | 最好情况 | 最坏情况 | 空间复杂度 | 稳定性 |
---|---|---|---|---|---|
希尔排序 | O(n log n) | O(nlog2n) | O(nlog2n) | O(1) | 不稳定 |
PS:希尔排序是对插入排序的优化
(8)归并排序
把数组不断细分到最小单位再根据需要组合
import java.util.Arrays;
import java.util.Scanner;
public class Main
{
public static void main(String[] args)
{
Scanner sca = new Scanner(System.in);
int n = sca.nextInt();
int [] arr = new int [n];
for (int i = 0; i < n; i++)//随机生成n个1000以内的数
{
arr[i] = (int)(Math.random() * 1000);
}
mergeSort(arr, 0, n - 1);//归并排序
System.out.println(Arrays.toString(arr));//输出
sca.close();
}
/**
* 分和治结合
* @param arr 待排序数组
* @param left 左
* @param right 右
*/
public static void mergeSort(int [] arr, int left, int right)
{
if (left < right)
{
int mid = (left + right) / 2;
mergeSort(arr, left, mid);//左递归
mergeSort(arr, mid + 1, right);//右递归
merge(arr, left, mid, right);//治
}
}
/**
* 治
* @param arr 待排序数组
* @param left 左
* @param mid 中间
* @param right 右
*/
public static void merge(int [] arr, int left, int mid, int right)
{
int l = left;
int r = mid + 1;
int t = 0;
int [] temp = new int [right - left + 1];
while (l <= mid && r <= right)
{
if (arr[l] <= arr[r])//两个子数组从左到右比较,哪个小就先插入到临时数组
{
temp[t] = arr[l];
t++;
l++;
}
else
{
temp[t] = arr[r];
t++;
r++;
}
}
while (l <= mid)//左数组如果还有剩余直接把全部插入到临时数组
{
temp[t] = arr[l];
t++;
l++;
}
while (r <= right)//右数组如果还有剩余直接把全部插入到临时数组
{
temp[t] = arr[r];
t++;
r++;
}
t = 0;
for (int i = left; i <= right; i++)//更新数组left到right位置,而不是全部数组
{
arr[i] = temp[t];
t++;
}
}
}
排序算法 | 平均时间复杂度 | 最好情况 | 最坏情况 | 空间复杂度 | 稳定性 |
---|---|---|---|---|---|
归并排序 | O(n log n) | O(n log n) | O(n log n) | O(n) | 稳定 |
PS:分治思想