希尔排序
希尔排序的时间复杂度十分复杂,它与分组序列有关,本这次的分组序列为 4,2,1,即 N / (2的n次方)
最坏的情况为O(n²)一般是 O(n)< t <=O(n²)
其他的分组序列对应的时间复杂度,可百度查看相关资料。
/*
希尔排序是一种分组插入排序算法。比插入排序快
首先取一个整数d1=n/2,将元素分为d1个组,每组相邻量元素之间距离为d1,在各组内进行直接插入排序
取第二个整数d2=d1/2,重复上述分组排序过程,直到di=1,即所有元素在同一组内进行直接插入排序
希尔排序每趟并不使某些元素有序,而是使整体数据趋近有序,最后一趟使所有数据有序
*/
public class Main{
public static void main(String[] args) {
int[] arr = {5, 1, 6, 9, 4, 8, 2, 3, 7};
//调用希尔排序函数
ShellSort(arr);
//输出结果
for (int i : arr) {
System.out.print(i+", ");
}
}
// 希尔排序(分组的插入排序)
public static void ShellSort(int[] arr){
int d = arr.length;
while (d>=1){
InsertSortGap(arr, d);
d /= 2;
}
}
// 插入排序
public static void InsertSortGap(int[] arr, int gap){
int tmp;
int j;
for (int i = gap; i < arr.length; i++) {
tmp = arr[i];
j = i-gap;
while (j>=0 && arr[j]>tmp){
arr[j+gap] = arr[j];
j -= gap;
}
arr[j+gap] = tmp;
}
}
}
/* ↓ ↓ ↓ 5,4,7间距相差都为d(第一次的d为4)
元素数据 :5, 1, 6, 9, 4, 8, 2, 3, 7
分组间隔4: 4, 1, 2, 3, 5, 8, 6, 9, 7, 第一次分组
分组间隔2: 2, 1, 4, 3, 5, 8, 6, 9, 7, 第二次分组
分组间隔1: 1, 2, 3, 4, 5, 6, 7, 8, 9, 第三次分组
第一次分组:(分组依据就是每个元素之间距离为d)
5, 4, 7 //第一组
1, 8, //第二组
6, 2, //第三组
9, 3, //第四组
每组分别进行插入排序后
4, 5, 7 //第一组
1, 8, //第二组
2, 6, //第三组
3, 9, //第四组
分组间隔4排完后: 4, 1, 2, 3, 5, 8, 6, 9, 7,
然后继续分成2组
4, 2, 5, 6, 7, 第一组
1, 3, 8, 9, 第二组
每组分别进行插入排序后
2, 4, 5, 6, 7,
1, 3, 8, 9,
分组间隔2排完后:2, 1, 4, 3, 5, 8, 6, 9, 7,
可以看出每次分组排序后,并不是某个元素到达正确的顺序,
而是整体在不断趋于顺序,直到最后分组间隔为1时(即一次普通的插入排序)排序后使整体彻底排好顺序
*/
计数排序
时间复杂度:O(n)
/*
对列表进行排序,已知列表中的数范围。
*/
public class Main{
public static void main(String[] args) {
// 随机生成一个长度25,元素在[1,10]的集合
ArrayList<Integer> arr = GetArr();
// 排序前集合
System.out.print("排序前:");
for (int i : arr) {
System.out.print(i+", ");
}
System.out.println();
// 创建一个长度等于集合中最大值的数组
// 对集合中的数进行统计
int[] count = new int[Collections.max(arr)+1];
for (int i :arr){
count[i]++;
}
//输出count数组
//System.out.print("统计结果,下标代表数,元素代表该数的个数:");
//for (int i : count) {
// System.out.print(i+", ");
//}
//System.out.println();
//清空集合后重新写入
arr.clear();
//这里虽然是嵌套循环,但是写入的次数总共是n(原arr长度)次
for (int i = 0; i < count.length; i++) {
// 将元素i 写入count[i]次
while (count[i]>0){
arr.add(i);
count[i]--;
}
}
//输出排序结果
System.out.print("排序后:");
for (int i : arr) {
System.out.print(i+", ");
}
System.out.println();
}
// 获取随机集合
public static ArrayList<Integer> GetArr(){
ArrayList<Integer> arr = new ArrayList<>();
for (int i = 1; i <= 25; i++) {
int a = (int)(Math.random()*10+1);
arr.add(a);
}
return arr;
}
}
/*
排序前:5, 8, 8, 7, 2, 2, 1, 6, 3, 6, 2, 5, 3, 1, 6, 5, 5, 9, 7, 7, 7, 3, 7, 5, 1,
排序后:1, 1, 1, 2, 2, 2, 3, 3, 3, 5, 5, 5, 5, 5, 6, 6, 6, 7, 7, 7, 7, 7, 8, 8, 9,
计数排序虽然时间复杂度为O(n)但是要使用它本身就有很多限制
1.必须知道要排序的数组元素范围
2.消耗空间大,假设只有10个数但是是0到1亿,那么在统计时就要开长度为1亿的数组。
*/
桶排序
桶排序的时间复杂度与桶的分配有关
平均情况时间复杂度:O(n+k)
最坏情况时间复杂度:O(n²k)
空间复杂度:O(nk)
public class Main{
public static void main(String[] args) {
// 随机生成一个长度150,元素在[1,100]的集合
ArrayList<Integer> arr = GetArr();
int max_num = Collections.max(arr);
// 排序前集合
System.out.print("排序前:");
for (int i : arr) {
System.out.print(i+", ");
}
System.out.println();
//调用桶排序函数这里分了10个桶
// 也可以写成 bucket_num = (Collections.max(arr) - Collections.min(arr)) / arr.size() + 1
BucketSort(arr, 10, max_num);
}
// 桶排序
public static void BucketSort(ArrayList<Integer> arr, int bucket_num, int max_num){
// 创建桶
ArrayList<ArrayList<Integer>> buckets = new ArrayList<>(bucket_num);
for(int i = 0; i < bucket_num; i++){
buckets.add(new ArrayList<>());
}
int i; // 桶号
for (int val : arr){
i = Math.min(val /(max_num/bucket_num), bucket_num-1);
buckets.get(i).add(val);
// 维持桶中数据的顺序,这一步也可以分开写,先将元素全部装入桶,再对每个桶进行排序
// 这里用自带的排序函数,也可以用之前写的冒泡排序等
Collections.sort(buckets.get(i));
}
System.out.print("排序后:");
System.out.println(buckets);
}
// 获取随机集合
public static ArrayList<Integer> GetArr(){
ArrayList<Integer> arr = new ArrayList<>();
for (int i = 1; i <= 150; i++) {
int a = (int)(Math.random()*100+1);
arr.add(a);
}
return arr;
}
}
/*
排序前:81, 17, 12, 77, 15, 55, 78, 76, 26, 27, 22, 84, 75, 60, 79, 90, 46, 72, 15, 1, 62, 68, 97, 47, 65, 23, 64, 35, 3, 78, 49, 24, 20, 32, 69, 61, 97, 49, 11, 6, 94, 37, 49, 54, 74, 9, 80, 55, 21, 72, 47, 94, 66, 67, 26, 25, 35, 8, 80, 15, 61, 59, 56, 60, 75, 8, 89, 57, 84, 24, 79, 77, 62, 72, 90, 35, 19, 13, 57, 43, 14, 18, 13, 34, 54, 63, 74, 89, 70, 12, 79, 97, 34, 90, 65, 11, 26, 46, 79, 76, 44, 84, 78, 63, 19, 32, 21, 35, 50, 21, 71, 40, 100, 51, 69, 50, 83, 5, 50, 1, 94, 93, 13, 45, 39, 17, 24, 79, 24, 48, 10, 32, 19, 36, 21, 91, 55, 68, 3, 75, 57, 54, 75, 29, 88, 86, 14, 88, 85, 100,
排序后:[
1号桶:[1, 1, 3, 3, 5, 6, 8, 8, 9],
2号桶:[10, 11, 11, 12, 12, 13, 13, 13, 14, 14, 15, 15, 15, 17, 17, 18, 19, 19, 19],
3号桶:[20, 21, 21, 21, 21, 22, 23, 24, 24, 24, 24, 25, 26, 26, 26, 27, 29],
4号桶:[32, 32, 32, 34, 34, 35, 35, 35, 35, 36, 37, 39],
5号桶:[40, 43, 44, 45, 46, 46, 47, 47, 48, 49, 49, 49],
6号桶:[50, 50, 50, 51, 54, 54, 54, 55, 55, 55, 56, 57, 57, 57, 59],
7号桶:[60, 60, 61, 61, 62, 62, 63, 63, 64, 65, 65, 66, 67, 68, 68, 69, 69],
8号桶:[70, 71, 72, 72, 72, 74, 74, 75, 75, 75, 75, 76, 76, 77, 77, 78, 78, 78, 79, 79, 79, 79, 79],
9号桶:[80, 80, 81, 83, 84, 84, 84, 85, 86, 88, 88, 89, 89],
10号桶:[90, 90, 90, 91, 93, 94, 94, 94, 97, 97, 97, 100, 100]
]
桶排序的时间复杂度与桶的分配有关
假设有100个数据,其中90个数据都是在90~100那么几乎所有的数都将分到最后一个桶中,就失去了分桶的意义。
所以桶排序适用于数据分布比较均匀的情况,当然在知道数据分布的情况时我们可以人为的设置桶的分配情况
假设有100个数据,还是分10个桶,其中90个数据都是在90~100
那么可以针对这90个数进行分桶,例如第一个桶的范围是[0,89],剩下的9个桶都分到这90个数上去,每个桶装10个数。
*/
基数排序
时间复杂度:O(kn)
空间复杂度:O(k+n)
k: 数字位数
public class Main{
public static void main(String[] args) {
ArrayList<Integer> arr = GetArr();
RadixSort(arr);
}
// 基数排序
public static void RadixSort(ArrayList<Integer> arr){
int max_num = Collections.max(arr); // 确定循环次数
int it = 0, digit;
//从个位开始,每循环一次就上升一位,当超过最大数值的位数时退出循环
// 10**0 = 1, 10**1 = 10, 10**2 = 100 (python中**代表次方)
while (Math.pow(10,it) <= max_num){
//分桶 0,1,2,3,4,5,6,7,8,9
//分10个桶,因为每一位数字的范围是[0,9]
ArrayList<ArrayList<Integer>> buckets = new ArrayList<>(10);
for (int i = 0; i < 10; i++){
buckets.add(new ArrayList<>());
}
//排序
for (Integer val : arr){
digit = (val / (int)Math.pow(10,it)) % 10; // 获取当前位的数字
buckets.get(digit).add(val);
}
// 更新原集合顺序
arr.clear();
for(ArrayList<Integer> bucket : buckets){
arr.addAll(bucket);
}
it++;
}
System.out.println("排序后:"+arr);
}
// 获取随机集合
public static ArrayList<Integer> GetArr(){
ArrayList<Integer> arr = new ArrayList<>();
for (int i = 1; i <= 100; i++) {
int a = (int)(Math.random()*100+1);
arr.add(a);
}
System.out.println("排序前:"+arr);
return arr;
}
}
/*
排序前:[31, 21, 90, 61, 46, 95, 49, 39, 21, 37, 81, 80, 4, 2, 25, 42, 20, 44, 69, 24, 26, 17, 80, 29, 62, 27, 47, 83, 100, 22, 3, 60, 89, 74, 17, 29, 40, 67, 52, 82, 85, 84, 38, 70, 19, 47, 79, 17, 65, 34, 16, 78, 71, 95, 10, 20, 18, 46, 53, 44, 27, 18, 16, 65, 5, 23, 43, 19, 24, 82, 67, 56, 33, 59, 73, 69, 14, 46, 43, 72, 63, 26, 56, 49, 39, 68, 43, 82, 36, 90, 25, 47, 80, 50, 27, 94, 6, 30, 45, 85]
每次排序桶内的情况:
按个位排:[
0桶:[90, 80, 20, 80, 100, 60, 40, 70, 10, 20, 90, 80, 50, 30],
1桶:[31, 21, 61, 21, 81, 71],
2桶:[2, 42, 62, 22, 52, 82, 82, 72, 82],
3:[83, 3, 53, 23, 43, 33, 73, 43, 63, 43],
4:[4, 44, 24, 74, 84, 34, 44, 24, 14, 94],
5:[95, 25, 85, 65, 95, 65, 5, 25, 45, 85],
6:[46, 26, 16, 46, 16, 56, 46, 26, 56, 36, 6],
7:[37, 17, 27, 47, 17, 67, 47, 17, 27, 67, 47, 27],
8:[38, 78, 18, 18, 68],
9:[49, 39, 69, 29, 89, 29, 19, 79, 19, 59, 69, 49, 39]
]
按十位排:[
0 [100, 2, 3, 4, 5, 6],
1 [10, 14, 16, 16, 17, 17, 17, 18, 18, 19, 19],
2 [20, 20, 21, 21, 22, 23, 24, 24, 25, 25, 26, 26, 27, 27, 27, 29, 29],
3 [30, 31, 33, 34, 36, 37, 38, 39, 39],
4 [40, 42, 43, 43, 43, 44, 44, 45, 46, 46, 46, 47, 47, 47, 49, 49],
5 [50, 52, 53, 56, 56, 59],
6 [60, 61, 62, 63, 65, 65, 67, 67, 68, 69, 69],
7 [70, 71, 72, 73, 74, 78, 79],
8 [80, 80, 80, 81, 82, 82, 82, 83, 84, 85, 85, 89],
9 [90, 90, 94, 95, 95]
]
按百位排:[
0 [2, 3, 4, 5, 6, 10, 14, 16, 16, 17, 17, 17, 18, 18, 19, 19, 20, 20, 21, 21, 22, 23, 24, 24, 25, 25, 26, 26, 27, 27, 27, 29, 29, 30, 31, 33, 34, 36, 37, 38, 39, 39, 40, 42, 43, 43, 43, 44, 44, 45, 46, 46, 46, 47, 47, 47, 49, 49, 50, 52, 53, 56, 56, 59, 60, 61, 62, 63, 65, 65, 67, 67, 68, 69, 69, 70, 71, 72, 73, 74, 78, 79, 80, 80, 80, 81, 82, 82, 82, 83, 84, 85, 85, 89, 90, 90, 94, 95, 95],
1 [100], [], [], [], [], [], [], [], [] (最大数是100,所有只有0,1号桶有数)
]
排序后:[2, 3, 4, 5, 6, 10, 14, 16, 16, 17, 17, 17, 18, 18, 19, 19, 20, 20, 21, 21, 22, 23, 24, 24, 25, 25, 26, 26, 27, 27, 27, 29, 29, 30, 31, 33, 34, 36, 37, 38, 39, 39, 40, 42, 43, 43, 43, 44, 44, 45, 46, 46, 46, 47, 47, 47, 49, 49, 50, 52, 53, 56, 56, 59, 60, 61, 62, 63, 65, 65, 67, 67, 68, 69, 69, 70, 71, 72, 73, 74, 78, 79, 80, 80, 80, 81, 82, 82, 82, 83, 84, 85, 85, 89, 90, 90, 94, 95, 95, 100]
*/
桶排序与基数排序的区别:
桶排序:
装一次桶,在桶中排序
基数排序:
装多次桶,不在桶中排序,只是装桶,输出,装桶,输出。。。
排序是因为桶是有序的(0,1,2······)0桶总是先输出,然后1桶,2桶·····
冒泡排序,选择排序,插入排序
https://blog.csdn.net/m0_60370702/article/details/123360290?spm=1001.2014.3001.5502
快速排序,归并排序,堆排序
https://blog.csdn.net/m0_60370702/article/details/123417783?spm=1001.2014.3001.5502