八大排序算法

Posted jxkun

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了八大排序算法相关的知识,希望对你有一定的参考价值。

目录

排序算法

排序算法之间的比较:

排序算法 最差时间分析 平均时间复杂度 稳定度 空间复杂度
冒泡排序 O(n^2) O(n^2) 稳定 O(1)
选择排序 O(n^2) O(n^2) 不稳定 O(1)
插入排序 O(n^2) O(n^2) 稳定 O(1)
归并排序 O(nlogn) O(nlogn) 稳定 O(n)
快速排序 O(n^2) O(nlogn) 不稳定 O(1)
堆排序 O(n^2) O(n*logn) 不稳定 O(1)
希尔排序 O(n^2) O(n^1.3) 不稳定 O(1)
桶排序 O(n) O(n) 稳定 O(n)

一、时间复杂度为O(n^2)的排序算法

1.1 冒泡排序(bubbleSort)

  • 进行n-1轮排序,每轮排序数组中元素依次两两交换, 获取最大的元素;
  • 时间复杂度 O(n^2), 空间复杂度O(1);
public class BubbleSort {
    public int[] bubbleSort(int[] A) {
        // write code here
        if(A == null || A.length < 2) return A;
        for(int i = 0; i < A.length - 1; i++){
            for(int j = 0; j < A.length - i - 1; j++){
                if(A[j] > A[j+1]){
                    swap(A, j, j+1);
                }
            }
        }
        return A;
    }
    
    public void swap(int[] A, int i1, int i2){
        int tmp = A[i1];
        A[i1] = A[i2];
        A[i2] = tmp;
    }
}

1.2 选择排序(selectionSort)

  • 进行n-1轮交换, 每轮交换获取最小的元素
  • 时间复杂度 O(n^2), 空间复杂度O(1);
public class SelectionSort {
    public int[] selectionSort(int[] A) {
        // write code here
        if(A == null || A.length < 2) return A;
        for(int i = 0; i < A.length - 1; i++){
            for(int j = i + 1; j < A.length; j ++){
                if(A[i] > A[j]){
                    swap(A, i, j);
                }
            }
        }
        return A;
    }
     public void swap(int[] A, int i1, int i2){
        int tmp = A[i1];
        A[i1] = A[i2];
        A[i2] = tmp;
    }
}

1.3 插入排序(insertionSort)

  • 时间复杂度 O(n^2), 空间复杂度O(1);
  • 当数组元素有序, 时间复杂度O(n)
public class InsertionSort {
    public int[] insertionSort(int[] A, int n) {
        // write code here
        for(int i = 1; i < A.length; i++){
            int key = A[i];
            int pre = i - 1;
            while(pre >= 0 && A[pre] > key){
                A[pre+1] = A[pre--];
            }
            A[pre + 1] = key;
        }
        return A;
    }
}

二、时间复杂度为O(log(n))的排序算法

2.1 归并排序(mergeSort)

  • 时间复杂度O(log(n)), 空间复杂度O(n)
  • 归并排序的时间复杂度与排序数组元素顺序无关;
public class MergeSort {
    public int[] mergeSort(int[] A, int n) {
        // write code here
        if(A == null || A.length < 2) return A;
        int[] tmp = new int[A.length];
        divide(A, 0, A.length - 1, tmp);
        return A;
    }
    // 数组合并
    public void merge(int[] A, int left, int right, int mid, int[] tmp){
        for(int i = left; i <= right; i++){
            tmp[i] = A[i];
        }
        int l1 = left, l2 = mid+1, index = left;
        for(int i = left; i <= right; i++){
            if(l1 > mid){
                A[index++] = tmp[l2++];
            }else if(l2 > right){
                A[index++] = tmp[l1++];
            }else if(tmp[l1] < tmp[l2]){
                A[index++] = tmp[l1++];
            }else{
                A[index++] = tmp[l2++];
            }
        }
    }
    
    public void divide(int[] A, int left, int right, int[] tmp){
        if(left < right){
            // 与 mid = (left + right)/2 相比, 可以防止left+right超出int范围
            int mid = left + (right-left)/2;
            divide(A, left, mid, tmp);
            divide(A, mid+1, right, tmp);
            merge(A, left, right, mid, tmp);
        }
    }
}

2.2 快速排序(quickSort)

最优情况: 元素分布均匀, 每次都能取到中间位置

  • 时间复杂度O(log(n)), 空间复杂度O(logn);

最坏情况: 时间复杂度O(n^2), 空间复杂度O(n), 以下三种最坏情况

  • 数组已经是正序(same order)排过序的。
  • 数组已经是倒序排过序的。
  • 所有的元素都相同(1、2的特殊情况)
public class QuickSort {
    public int[] quickSort(int[] A, int n) {
        // write code here
        quick(A, 0, A.length - 1);
        return A;
    }
    
    public void quick(int[] A, int left, int right){
        if(left >= right) return ;
        int key = A[left];
        int head = left, tail = right+1;
        while(true){
            while(A[++head] <= key) if(head == right) break;
            while(A[--tail] > key) if(tail == left) break;
            if(head >= tail) break;
            int tmp = A[head];
            A[head] = A[tail];
            A[tail] = tmp;
        }
        A[left] = A[tail];
        A[tail] = key;
        quick(A, left, tail-1);
        quick(A, tail + 1,right);
    }
}

2.3 堆排序

  • 时间复杂度O(nlog(n)), 空间复杂度O(1)

  • 最差情况, 数组元素已经是从小到大有序的, 时间复杂度O(n^2)

public class HeapSort {
    public int[] heapSort(int[] A, int n) {
        // 建堆
        for(int i = A.length/2; i >= 0; i--){
            heapAdjust(A, i, A.length);
        }
        for(int i = A.length - 1; i >= 0; i--){
            int head = A[0];
            A[0] = A[i];
            A[i] = head;
            heapAdjust(A, 0, i);
        }
        return A;
    }
    // 堆调整 O(log(n))
    public void heapAdjust(int[] A, int parent, int len){
        int head = A[parent];
        while(parent*2+1 < len){
            int childLeft = parent*2 + 1;
            if(childLeft + 1 < len && A[childLeft + 1] > A[childLeft]){
                childLeft++;
            }
            if(A[childLeft] < head) break;
            A[parent] = A[childLeft];
            parent = childLeft;
        }
        A[parent] = head;
    }
}

2.4 希尔排序(shellSort)

  • Shell排序比起QuickSort,MergeSort,HeapSort慢很多。适合于数据量在5000以下并且速度并不是特别重要的场合。它对于数据量较小的数列重复排序是非常好的。

  • 时间复杂度O(nlog(n)),空间复杂度O(1)
public class ShellSort {
    public int[] shellSort(int[] A, int n) {
        for(int i = n; i >= 1; i /= 2){
            for(int j = i; j < n; j++){
                int key = A[j];
                int pre = j - i;
                while(pre >= 0 && A[pre] > key){
                    A[pre + i] = A[pre];
                    pre -= i;
                }
                A[pre + i] = key;
            }
        }
        return A;
    }
}

三、 桶排序(时间复杂度O(n))

3.1 计数排序

  • 计数排序适用于元素在一定有限范围内的数组;
  • 时间复杂度O(n), 空间复杂度 O(m)
import java.util.*;

public class CountingSort {
    public int[] countingSort(int[] A, int n) {
        if(A == null || A.length < 2) return A;
        int min = A[0], max = A[0];
        for(int i = 0; i < A.length; i++){
            min = Math.min(min, A[i]);
            max = Math.max(max, A[i]);
        }
        int[] bucket = new int[max - min + 1];
        // 入桶
        for(int i = 0; i < A.length; i++){
            bucket[A[i] - min]++;
        }
        // 出桶
        int index = 0;
        for(int i = 0; i < bucket.length; i++){
            while(bucket[i]-- > 0){
                A[index++] = i + min;
            }
        }
        return A;
    }
}

3.2 基数排序

  • 时间复杂度O(n), 空间复杂度O(n)
// 保证元素均小于等于2000的基数排序
public class RadixSort {
    public int[] radixSort(int[] A, int n) {
        if(A == null || n < 2) return A;
        int[][] bucket = new int[10][n]; // 创建10个桶,每个桶需要的长度都有可能为n
        int[] count = new int[10];  // 记录每个对应桶中装入的元素个数
        
        for(int i = 1; i <= 1000; i *= 10) {
            for(int k = 0; k < n; k++) {
                int num = A[k] / i % 10; // 获取位数上对应数字
                bucket[num][count[num]++] = A[k];
            }
            // 从bucket中将数据倒回A中
            int index = 0;
            for(int k = 0; k < 10; k++) {
                for(int j = 0; j < count[k]; j++) {
                    A[index++] = bucket[k][j];
                }
                count[k] = 0; // 重置为0
            }
        }
        return A;
    }
}

以上是关于八大排序算法的主要内容,如果未能解决你的问题,请参考以下文章

C语言实现八大排序算法

数据结构初阶第九篇——八大经典排序算法总结(图解+动图演示+代码实现+八大排序比较)

数据结构初阶第九篇——八大经典排序算法总结(图解+动图演示+代码实现+八大排序比较)

八大排序算法C语言过程图解+代码实现(插入,希尔,选择,堆排,冒泡,快排,归并,计数)

八大经典排序算法的代码实现

《糊涂算法》之八大排序——希尔排序