您现在的位置是:首页 >技术教程 >排序算法总结网站首页技术教程

排序算法总结

无与伦比的jyk 2025-03-24 12:01:02
简介排序算法总结

堆结构

完全二叉树

左孩子 2*i+1

右孩子2*i+2

父节点 i-1/2

大根堆

每棵子树最大值是头结点的值

小根堆

每棵子树最小值是头结点的值

heapInsert方法

O(log N)

与父节点比较(i-1)/2

public static void headInsert(int []arr,int index){
    while(arr[index] > arr[(i-1)/2]){
        swap(arr,index,(index-1)/2);
        index = (index - 1)/2;
   }
}
heapify方法

O(log N)

public static void heapify(int[] arr,int index,int heapSize){
    int left = index * 2 +1;
    while(left < heapSize){
        int largest = left + 1 < heapSize && arr[left + 1] > arr[left]
            ?left + 1 : left;//比较左右孩子
        
        largest = arr[largest] > arr[index] ? largest:index;//比较父亲和较大孩子之间
        
        if(largest == index){
            break;
        }
        swap(arr,largest,index); 
        index = largest;
        left = index * 2 + 1;
        
    }
}
heapSort方法
public static void heapSort(int[] arr){
    if(arr == null || arr.length < 2){
        return;
    }
    for(int i = 0;i < arr.length;i++){ //O(N)
        heapInsert(arr,i);  //O(logN)
    }
    int  heapSize = arr.length;
    swap(arr,0,--heapSize);
    while(heapSize > 0){         //O(N)
        heapify(arr,0,heapSize); //O(logN)
        swap(arr,0,--heapSize);  //O(1)
    }
}
题目

一个几乎有序的数组,几乎有序是指,如果把数组排好序,每个元素移动的距离不超过k,并且k相对数组来说较小,选择一个合适的排序算法

public class Code04{
    priorityQueue<Integer>heap = new PriorityQueue<>();
    int index = 0;
    for(;index <=
        Math.min(arr.length,k);index++){
        heap.add(arr[index]);
    }
    int i = 0;
    for(;index < arr.length;i++,index++){
        heap.add(arr[index]);
        arr[i] = heap.poll();
    }
    while(!heap.isEmpty()){
        arr[i++] = heap.poll();
    }
}

系统自带的堆结构相当于一个黑盒,支持add或者poll的基础操作,不支持以很小的代价调整其中一个数还维持堆结构,手写的可以,所以某些情况要手写堆结构来提高效率

计数排序

不基于比较排序

对于特殊数据可以使用计数排序,例如针对员工年龄进行排序,假设员工年龄范围为0-100岁,那么准备一个0-100的数组,遍历一遍原数组,在计数数组中按顺序记录年龄出现的频次,再将词频还原成有序的数组,本质上是一种特殊的桶排序

时间复杂度为O(N)

基数排序

将数据从左往右依次按照个位数大小放桶,再按顺序倒出来,先进先出,再按照十位数依次进桶,按顺序倒出来,最后按照百位数进桶 ,倒出来排好序

 public static void radixSort(int[] arr){
        if (arr == null || arr.length < 2) {
            return;
        }
        radixSort(arr, 0, arr.length - 1, maxbits(arr));
    }


    public static int maxbits(int[] arr)
    {
        int max = Integer.MIN_VALUE;
        for (int i = 0; i < arr.length - 1; i++) {
            max = Math.max(max, arr[i]);
        }
        int res = 0;
        while (max != 0) {
            res++;
            max /= 10;
        }
        return res;
    }


    public static void radixSort(int[] arr, int L, int R, int digit)
    {
        final int radix = 10;
        int i, j = 0;
        int[] bucket = new int[R - L + 1];

        for (int d = 1; d <= digit; d++) {
            int[] count = new int[radix];
            for (i = L; i <= R; i++) {
                j = getDigit(arr[i], d);
                count[j]++;
            }
            for (i = 1; i < radix; i++) {
                count[i] = count[i] + count[i - 1];
            }
            for (i = R; i >= L; i--) {
                j = getDigit(arr[i], d);
                bucket[count[j] - 1] = arr[i];
                count[j]--;
            }
            for (i = L, j = 0; i <= R; i++, j++) {
                arr[i] = bucket[j];
            }
        }
    }

    public static int getDigit(int x, int d)
    {
        return ((x / ((int)Math.pow(10, d - 1))) % 10);
    }
排序算法的稳定性及其汇总

同样值的个体之间,如果不因为排序而改变相对次序,那么这个排序是有稳定性的,否则就没有

不具备稳定性的排序:选择,快速,堆

具备稳定性:冒泡(相等不交换),插入 (相等不交换),归并(左指针优先),一切桶排序思想下的排序

一般会选择使用快速排序,因为经过实验的结果,快排的常数项最低,实在是有空间的限制可以用堆排,或者对稳定性有要求的用归并排序
在这里插入图片描述

1.归并排序的空间复杂度可以变成O(1),不需要掌握,但是损失稳定性

2.原地归并排序空间复杂度变成O(1),时间复杂度变成O(N^2);,不如快排

3.快速排序可以稳定性,但是空间复杂度变成O(N),不如归并

4.改进都不重要,目前没有时间复杂度为O(N*logN),额外空间复杂度O(1),还具有稳定性的排序

5.有一道题目,奇数在数组左边,偶数在数组右边,求原始相对次序不变,时间复杂度O(N),空间复杂度O(1)

经典快排的paration做不到稳定性,01标准和奇偶标准是同一个标准,所有做不到

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

工程程序上排序的改进

1.充分利用O(N*logN)和O(N^2)排序各自的优势

2.稳定性的考虑
.有一道题目,奇数在数组左边,偶数在数组右边,求原始相对次序不变,时间复杂度O(N),空间复杂度O(1)

经典快排的paration做不到稳定性,01标准和奇偶标准是同一个标准,所有做不到

风语者!平时喜欢研究各种技术,目前在从事后端开发工作,热爱生活、热爱工作。