【无标题】

常用排序算法复习

排序中的对数器

import java.util.Arrays;

public class Main {
    public static void main(String[] args) {
        int times = 100000;
        int maxValue = 1000;
        int size = 1000;
        for (int i = 0; i < times; i++) {
            int[] arr = getRandomArr(size, maxValue);
            int[] copyArr = arr.clone();  // 深拷贝一个数组
            Arrays.sort(arr);
            bubbleSort(copyArr);
            
            if (!isEqualArr(arr, copyArr)) {
                System.out.println("fuck!!!");
                break;
            }
        }
    }
    public static void bubbleSort(int[] arr) {
        if (arr == null || arr.length < 2) {
            return;
        }
        int n = arr.length;
        for (int j = n - 1; j >= 0; j--) {
            for (int i = 0; i < j; i++) {
                if (arr[i] > arr[i + 1]) {
                    swap(arr, i, i + 1);
                }
            }
        }
    }
    public static void swap(int[] arr, int i, int j) {
        int temp = arr[i];
        arr[i] = arr[j];
        arr[j] = temp;
    }
    public static int[] getRandomArr(int size, int maxValue) {
        int[] arr = new int[(int)(Math.random() * size)];
        for (int i = 0; i < arr.length; i++) {
            arr[i] = (int)(Math.random() * maxValue - Math.random() * maxValue);
        }
        return arr;
    }
    // 验证两个数组的值是否相同
    public static boolean isEqualArr(int[] arr1, int[] arr2) {
        if (arr1 == null || arr2 == null) {
            return arr1 == null && arr2 == null;
        }
        if (arr1.length != arr2.length) {
            return false;
        }
        int n = arr1.length;
        for (int i = 0; i < n; i++) {
            if (arr1[i] != arr2[i]) {
                return false;
            }
        }
        return true;
    }
}

1、冒泡排序

每次将最大的冒泡到最后一个

public static void bubbleSort(int[] arr) {
    if (arr == null || arr.length < 2) {
        return;
    }
    int n = arr.length;
    for (int j = n - 1; j >= 0; j--) {
        for (int i = 0; i < j; i++) {
            if (arr[i] > arr[i + 1]) {
                swap(arr, i, i + 1);
            }
        }
    }
}

2、选择排序

每次选择最大的与最后一个交换

public static void selectSort(int[] arr) {
    if (arr == null || arr.length < 2) {
        return;
    }
    int n = arr.length;
    for (int j = n - 1; j > 0; j--) {
        int maxIndex = j;
        for (int i = 0; i <= j; i++) {
            if (arr[i] > arr[maxIndex]) {
                maxIndex = i;
            }
        }
        swap(arr, j, maxIndex);
    }
}
public static void swap(int[] arr, int i, int j) {
    int temp = arr[i];
    arr[i] = arr[j];
    arr[j] = temp;
}

3、插入排序

每到一个结尾就对其及其之前的进行排序

public static void insertSort(int[] arr) {
    if (arr == null || arr.length < 2) {
        return;
    }
    int n = arr.length;
    for (int j = 1; j < n; j++) {
        for (int i = j; i > 0; i--) {
            if (arr[i] < arr[i - 1]) {
                swap(arr, i - 1, i);
            } else {
                break;
            }
        }
    }
}
public static void swap(int[] arr, int i, int j) {
    int temp = arr[i];
    arr[i] = arr[j];
    arr[j] = temp;
}

4、希尔排序

将整个序列通过指定增量gap分成若干段,分别进行插入排序,gap不断缩小,最后为1,排序完成

public static void shellSort(int[] arr) {
    if(arr == null || arr.length < 2) {
        return;
    }
    int n = arr.length;
    for (int gap = arr.length / 2; gap > 0; gap /= 2) {
        // 插入排序
        for (int j = gap; j < n; j += gap) {
            for (int i = j; i >= gap; i-= gap) {
                if (arr[i] < arr[i - gap]) {
                    swap(arr, i, i - gap);
                } else {
                    break;
                }
            }
        }
    }
}

5、归并排序

不断二分,然后合并

public static void mergeSort(int[] arr) {
    if (arr == null || arr.length < 2) {
        return;
    }
    merge(arr, 0, arr.length - 1);
}
public static void merge(int[] arr, int l, int r) {
    if (l == r) {
        return;
    }
    int mid = l + ((r - l) >> 1);
    merge(arr, l, mid);
    merge(arr, mid + 1, r);
    int[] temp = new int[r - l + 1];
    int p1 = l;
    int p2 = mid + 1;
    int p = 0;
    while (p1 <= mid && p2 <= r) {
        temp[p++] = arr[p1] < arr[p2] ? arr[p1++] : arr[p2++];
    }
    while (p1 <= mid) {
        temp[p++] = arr[p1++];
    }
    while (p2 <= r) {
        temp[p++] = arr[p2++];
    }
    for (int i = 0; i < temp.length; i++) {
        arr[i + l] = temp[i];
    }
}

6、快速排序

不断partition,不断递归即可

public static void fastSort(int[] arr) {
    if (arr == null || arr.length < 2) {
        return;
    }
    partition(arr, 0, arr.length - 1);
}
public static void partition(int[] arr, int l, int r) {
    if (l >= r) {
        return;
    }
    int randomIndex = l + (int)(Math.random() * (r - l));
    swap(arr, r, randomIndex);
    int num = arr[r];
    int left = l - 1;
    int right = r;
    int i = l;
    while (i < right) {
        if (arr[i] < num) {
            swap(arr, i++, ++left);
        } else if (arr[i] > num) {
            swap(arr, i, --right);
        } else {
            i++;
        }
    }
    swap(arr, r, right);
    partition(arr, l, left);
    partition(arr, right + 1, r);
}
public static void swap(int[] arr, int i, int j) {
    int temp = arr[i];
    arr[i] = arr[j];
    arr[j] = temp;
}

7、堆排序

构建大根堆,每次取出堆的根,然后拿堆的最后一个元素替换堆根并调整堆即可

 public static void heapSort(int[] arr) {
     if (arr == null || arr.length < 2) {
         return;
     }
     int n = arr.length;
     //        向上堆化
     //        for (int i = 0; i < n; i++) {
     //            heapifyUp(arr, i);
     //        }
     int heapSize = n - 1;
     //        向下堆化
     for (int i = n - 1; i >= 0; i--) {
         heapifyDown(arr, i, heapSize);
     }
     while (heapSize > 0) {
         swap(arr, 0, heapSize--);
         heapifyDown(arr, 0, heapSize);
     }
 }
public static void heapifyUp(int[] arr, int index) {
    // 向上堆化,找父节点
    while (arr[index] > arr[(index - 1) / 2]) {
        swap(arr, index, (index - 1) / 2);
        index = (index - 1) / 2;
    }
}
public static void heapifyDown(int[] arr, int index, int heapSize) {
    // 向下堆化
    int leftIndex = 2 * index + 1;
    while (leftIndex <= heapSize) {
        int largeChildIndex = leftIndex + 1 <= heapSize && arr[leftIndex + 1]
            > arr[leftIndex] ? leftIndex + 1 : leftIndex;
        int largeIndex = arr[largeChildIndex] > arr[index] ? largeChildIndex : index;
        if (largeIndex == index) {
            break;
        }
        swap(arr, index, largeIndex);
        index = largeChildIndex;
        leftIndex = 2 * index + 1;
    }
}
public static void swap(int[] arr, int i, int j) {
    int temp = arr[i];
    arr[i] = arr[j];
    arr[j] = temp;
}

8、桶排序

8.1 最简单的桶排序

1、将待排序元素划分到不同的桶。先扫描一遍序列求出最大值 maxV 和最小值 minV ,设桶的个数为 k ,则把区间 [minV, maxV] 均匀划分成 k 个区间,每个区间就是一个桶。将序列中的元素分配到各自的桶。
2、对每个桶内的元素进行排序。可以选择任意一种排序算法。
3、将各个桶中的元素合并成一个大的有序序列。
假设数据是均匀分布的,则每个桶的元素平均个数为 n/k 。假设选择用快速排序对每个桶内的元素进行排序,那么每次排序的时间复杂度为 O(n/klog(n/k)) 。总的时间复杂度为 O(n)+O(k)O(n/klog(n/k)) = O(n+nlog(n/k)) = O(n+nlogn-nlogk 。当 k 接近于 n 时,桶排序的时间复杂度就可以金斯认为是 O(n) 的。即桶越多,时间效率就越高,而桶越多,空间就越大。

public static void bucketSort(int[] arr) {
    if (arr == null || arr.length < 2) {
        return;
    }
    bucketSortProcess(arr, 10);
}
public static void bucketSortProcess(int[] arr, int k) {
    int max = Integer.MIN_VALUE;
    int min = Integer.MAX_VALUE;
    for (int n : arr) {
        max = Math.max(n, max);
        min = Math.min(n, min);
    }
    List<Integer>[] bucketList = new List[k];  
    for (int i = 0; i < k; i++) {
        bucketList[i] = new ArrayList<Integer>();
    }
    for (int n : arr) {
        int index = (k - 1) * (n - min) / (max - min);
        bucketList[index].add(n);
    }
    int index = 0;
    for (int i = 0; i < k; i++) {
        Collections.sort(bucketList[i]);
        for (int num : bucketList[i]) {
            arr[index++] = num;
        }
    }
}

8.2 基数排序

从低到高对每一位进行排序,每一次排序都要保证稳定性

public static int getBit(int num) {
    if (num == 0) {
        return 1;
    }
    int ans = 0;
    while (num != 0) {
        ans++;
        num /= 10;
    }
    return ans;
}
public static int getDigit(int num, int index) {
    // 返回一个数字十进制的某一位,从后往前数(从1开始)
    index --;
    while (index != 0) {
        num /= 10;
        index--;
    }
    return num % 10;
}
public static void radixSortInPositive(int[] arr) {
    // 对正数进行基数排序
    if (arr == null || arr.length < 2) {
        return;
    }
    int mostBit = 1;  // 获取数组中的数字最大的有多少位
    for (int n : arr) {
        mostBit = Math.max(mostBit, getBit(n));
    }
    int[] temp = new int[arr.length];  // 辅助数组
    for (int i = 1; i <= mostBit; i++) {
        int[] countArr = new int[10];
        for (int n : arr) {
            countArr[getDigit(n, i)]++;
        }
        for (int j = 1; j < 10; j++) {
            countArr[j] += countArr[j - 1];
        }
        // 此时必须要保证排序的稳定性
        // 因为百位数的排序过程中要保留十位数的排序结果
        for (int j = arr.length - 1; j >= 0; j--) {
            temp[--countArr[getDigit(arr[j], i)]] = arr[j];
        }
        for (int j = 0; j < temp.length; j++) {
            arr[j] = temp[j];
        }
    }
}
public static void radixSortInNegative(int[] arr) {
    // 对负数进行基数排序
    if (arr == null || arr.length < 2) {
        return;
    }
    int mostBit = 1;  // 获取数组中的数字最大的有多少位
    for (int n : arr) {
        mostBit = Math.max(mostBit, getBit(n));
    }
    int[] temp = new int[arr.length];  // 辅助数组
    for (int i = 1; i <= mostBit; i++) {
        int[] countArr = new int[10];
        for (int n : arr) {
            countArr[-getDigit(n, i)]++;
        }
        for (int j = 1; j < 10; j++) {
            countArr[j] += countArr[j - 1];
        }
        for (int j = arr.length - 1; j >= 0; j--) {
            temp[--countArr[-getDigit(arr[j], i)]] = arr[j];
        }
        for (int j = 0; j < temp.length; j++) {
            arr[j] = temp[j];
        }
    }
    for (int i = 0; i < arr.length; i++) {
        temp[i] = arr[arr.length - 1 - i];
    }
    for (int i = 0; i < arr.length; i++) {
        arr[i] = temp[i];
    }
}
public static void radixSort(int[] arr) {
    if (arr == null || arr.length < 2) {
        return;
    }
    int positiveNum = 0;
    int negativeNum = 0;
    for (int n : arr) {
        if (n >= 0) {
            positiveNum++;
        } else {
            negativeNum++;
        }
    }
    int[] positiveArr = new int[positiveNum];
    int[] negativeArr = new int[negativeNum];
    positiveNum = 0;
    negativeNum = 0;
    for (int n : arr) {
        if (n >= 0) {
            positiveArr[positiveNum++] = n;
        } else {
            negativeArr[negativeNum++] = n;
        }
    }
    radixSortInPositive(positiveArr);
    radixSortInNegative(negativeArr);
    for (int i = 0; i < negativeNum; i++) {
        arr[i] = negativeArr[i];
    }
    for (int i = 0; i < positiveNum; i++) {
        arr[i + negativeNum] = positiveArr[i];
    }

}

8.3 计数排序

使用一个长度为max-min+1的数组,每一个元素都是计数器,然后统计原始数组中各个数字的个数(计数排序)

public static void countSort(int[] arr) {
    if (arr == null || arr.length < 2) {
        return;
    }
    int max = Integer.MIN_VALUE;
    int min = Integer.MAX_VALUE;
    for (int n : arr) {
        max = Math.max(max, n);
        min = Math.min(min, n);
    }
    int N = max - min + 1;
    int[] countArr = new int[N];
    for (int n : arr) {
        countArr[n - min]++;
    }
    int j = 0;
    for (int i = 0; i < N; i++) {
        while (countArr[i] != 0) {
            arr[j++] = i + min;
            countArr[i]--;
        }
    }
}

桶排序总结

  • 计数排序就是当桶的个数为max-min+1的桶排序
  • 基数排序是对每一位进行桶排序

排序的稳定性

排序前后两个相等的数相对位置不变,则这种排序是稳定的,反之不稳定

各种排序对比

排序名称时间复杂度空间复杂度稳定性
冒泡排序O(n^2)O(1)稳定
选择排序O(n^2)O(1)稳定
插入排序O(n)~O(n^2)O(1)稳定
希尔排序O(nlog(n))O(K)稳定
归并排序O(nlog(n))O(n)稳定
快速排序O(nlog(n))O(log(n))~O(n)不稳定
堆排序O(nlog(n))O(1)不稳定
桶排序O(nlog(n/K))O(n)稳定
基数排序O(n)O(K)稳定
计数排序O(Kn)O(n+K)稳定
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

稽函数

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值