经典排序简介及比较

181 阅读6分钟

常见排序算法比较

常见的排序算法可以分为以下几种:

  1. 冒泡排序(Bubble Sort):重复遍历待排序的序列,比较相邻的两个元素大小,如果前一个元素比后一个元素大,则交换它们的位置,直到整个序列排序完成为止。
  2. 选择排序(Selection Sort):重复遍历待排序的序列,每次选择一个最小的元素,放到已排序的序列的末尾,直到整个序列排序完成为止。
  3. 插入排序(Insertion Sort):将待排序的序列分为已排序和未排序两个部分,从未排序的部分中取出第一个元素,将它插入到已排序部分的合适位置,直到整个序列排序完成为止。
  4. 快速排序(Quick Sort):选择一个基准元素,将序列中小于基准元素的放在它的左边,大于基准元素的放在它的右边,再递归地对左右两个子序列进行同样的操作,直到整个序列排序完成为止。
  5. 归并排序(Merge Sort):将待排序的序列分成两个部分,对每个部分递归地进行归并排序,然后将两个有序的子序列合并成一个有序的序列。
  6. 堆排序(Heap Sort):将待排序的序列构建成一个最大堆或最小堆,每次从堆中取出堆顶元素(最大值或最小值),然后重新构建堆,直到整个序列排序完成为止。
  7. 计数排序(Counting Sort):计数排序是一种非比较排序算法,它的基本思想是统计序列中每个元素出现的次数,然后根据每个元素的计数值来排序。
  8. 桶排序(Bucket Sort):桶排序是一种非比较排序算法,它的基本思想是将序列中的元素分配到若干个桶中,然后对每个桶中的元素进行排序,最后按照桶的顺序将所有桶中的元素依次连接起来,形成一个有序序列。
  9. 基数排序(Radix Sort):基数排序是一种非比较排序算法,它的基本思想是按照低位到高位的顺序,对序列中的元素进行排序。具体地,先根据元素的个位数字进行排序,然后根据十位数字进行排序,以此类推,直到最高位排序完成为止。
排序算法平均时间复杂度最优时间复杂度最差时间复杂度空间复杂度排序方式
冒泡排序O(n^O(n)O(n^2)O(1)In-
选择排序OO(n^2)O(n^2)O(1)In
插入排序OO(n)O(n^2)O(1)In-
快速排序O(nlogn)O(nlogn)O(n^2)O(logn)In-
归并排序O(nlognO(nlognO(nlogn)O(n)Out-
O(nlogn)O(nlogn)O(nlogn)O(1)In-
O(n+k)O(n+k)O(nO(n+k)Out-
桶排序O(n+k)O(n)O(n^2)OOut-
O(d*(n+k))O(d*(nO(d*(n+k))O(n+k)Out-

在排序算法中,如果经过排序后,两个相等的元素在排序前的相对位置没有发生改变,那么这个排序算法就是稳定排序算法。反之,如果经过排序后,两个相等的元素在排序前的相对位置发生了改变,那么这个排序算法就是不稳定排序算法。

排序算法稳定性
选择排
插入排序
归并
堆排序不稳定
计数排序稳定
桶排序稳定
基数排序稳定

具体算法举例

冒泡排序

/**
 * 一旦某一轮循环后一次调换都没有发生,证明已经有序
 */
//冒泡优化1
public static void bubbleSort(int[] array){
    if(array!=null&&array.length<2){
        return;
    }
    Boolean flag=false;
    for (int i = 0; i < array.length-1; i++) {
        for (int j = 0; j < array.length-i-1; j++) {
            if(array[j]>array[j+1]){
                flag= swopBubble(array,j,j+1);
            }
        }
        if(flag==false){
            break;
        }
    }
​
}

插入排序

/**
 * j固定在i的上一位,边界+条件 交换 左至右,首次大循环只看一次
 */
public static void insertSort(int[] array){
    if(array!=null&&array.length<2){
        return;
    }
    //j固定在i的上一位,边界+条件 交换 --
    for (int i = 1; i < array.length; i++) {
        for (int j = i-1; j >=0&&array[j]>array[j+1] ;j--) {
            swop(array,j,j+1);
​
        }
    }
}

快速排序

/**
 * 
 * 快排思路 portion
 * 当前数小于目标目标数,和小于区做交换,小于区右扩,当前位置跳下一个
 * 当前数等于目标数 直接跳下一个
 * 当前数大于目标,和大于区前一个做交换,大于区左扩 当前位置不变
 *
 */
public class QuickSort {
​
    public static void quick(int l,int r,int []arr){
        if(l>=r){   //比如 小于区没有右扩导致edges[0]=-1
            return;
        }
        int[] edges = quickPortion(l, r, arr);//edges 小于区最后一个 和 大于区第一个
        quick(l,edges[0],arr);
        quick(edges[1],r,arr);
    }
    public static int[] quickPortion(int l,int r,int[]arr){
        int less=l-1;
        int more=r;
        int cur=l;
        //从l到r上随机选一个值作为目标值,从而在数学期望上达到时间复杂度log(n)
        int random=(int)(Math.random()*(r-l+1))+l;
        swap(r,random,arr);
        while (cur<more){
            if(arr[cur]<arr[r]){
                swap(++less,cur++,arr);
            }else if(arr[cur]==arr[r]){
                cur++;
            }else {
                swap(--more,cur,arr);
            }
        }
        swap(r,more,arr);
        return new int[]{less,more+1};
    }

归并排序

/**
 * MergeSort 流程
 * f函数 递归调用
 * 1求M 2左递归 3右递归 4Merge
 * Merge 函数
 * 1l->m m+1->r 共同范围内
 * 2l->m 范围内 /m+1->r 范围内
 * 3help数组替换
 */
public static void mergeSort1(int l, int r, int[] arr) {
    if (l >= r) {
        return;
    }
    int m = l + ((r - l) >> 1);
    mergeSort1(l, m, arr);
    mergeSort1(m + 1, r, arr);
    merge(l, m, r, arr);
}
​
public static void merge(int l, int m, int r, int[] arr) {
    int length = r - l + 1;
    int[] help = new int[length];
    int left = l;
    int right = m + 1;
    int index = 0;
    while (left <= m && right <= r) {
        //从l 到r 以 m分为两组,各自从左向右 谁小先拷贝谁
        help[index++] = arr[left] <= arr[right] ? arr[left++] : arr[right++];
    }
    while (left <= m) { //right越界
        help[index++] = arr[left++];
    }
    while (right <= r) {//left越界
        help[index++] = arr[right++];
    }//merge后 拷贝回原数组
    System.arraycopy(help, 0, arr, l, length);
}

加强堆及堆排序

package review;
​
import java.util.*;
​
/**
 * 加强堆排序
 * 经典操作 heapyIfy 和 heapInsert
 *
 * @author wangyiming
 */
public class Heap<T> {
    public ArrayList<T> heap;
    public HashMap<T, Integer> indexMap;
    public int heapSize;
    public Comparator<? super T> comparator;
​
    public Heap(Comparator<? super T> comparator) {
        this.comparator = comparator;
        heapSize = 0;
        heap = new ArrayList<>();
        indexMap = new HashMap<>();
    }
​
    //is empty size contains peek
    public boolean isEmpty() {
        return heap.isEmpty();
    }
​
    public int size() {
        return heap.size();
    }
​
    public boolean contains(T key) {
        return indexMap.containsKey(key);
    }
​
    public T peek() {
        return heap.get(0);
    }
​
​
    public void heapInsert(int index) {
        //这里 如果当index向上跳 跳到了root节点,由于两者比较==0 也会停止循环
        while (comparator.compare(heap.get(index), heap.get((index - 1) / 2)) > 0) {
            swap(index, (index - 1) / 2);
            index = (index - 1) / 2;
        }
    }
​
    public void heapIfy(int index) {
        int left = 2 * index + 1;
        while (left < heapSize) {
            //largest 是left 和 right 中的大的那个
            int largest = left + 1 < heapSize ? (comparator.compare(heap.get(left), heap.get(left + 1)) > 0 ? left : left + 1) : left;
            if(comparator.compare(heap.get(index),heap.get(largest))>0){
                //最大的是夫节点
                break;
            }
            swap(index,largest);
            index=largest;
            left=2*index+1;
        }
    }
    public void arrayHeapIfy(int[]arr,int index,int heapSize){
        int left=2*index+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,index,largest);
            index=largest;
            left=2*index+1;
        }
    }
​
    public void swap(int[]arr,int i,int j){
        int t=arr[i];
        arr[i]=arr[j];
        arr[j]=t;
    }
​
    public void swap(int i, int j) {
        T t1 = heap.get(i);
        T t2 = heap.get(j);
        indexMap.put(t1, j);
        indexMap.put(t2, i);
        heap.set(i, t2);
        heap.set(j, t1);
    }
    public T pop(){
        T t=heap.get(0);
        //头尾互换
        swap(0,heapSize-1);
        indexMap.remove(t);
        heap.remove(--heapSize);
        heapIfy(0);
        return t;
    }
​
    public void resign(T obj){
        heapIfy(indexMap.get(obj));
        heapInsert(indexMap.get(obj));
    }
​
    public void push(T obj){
        heap.add(obj);
        indexMap.put(obj,heapSize);
        heapInsert(heapSize++);
    }
    public void remove(T obj){
        Integer index = indexMap.get(obj);  //要删除的位置
        T t = heap.get(--heapSize);     //末尾的值
        indexMap.remove(obj);           //删除目标的索引
        heap.remove(heapSize);          //删除末尾
        if(t!=obj){             //如果要删除的就是最后一个,就不用调整了
            indexMap.put(t,index);  //t插入到index
            heap.set(index,t);
            resign(t);
        }
    }
​
​
    public static void main(String[] args) {
        Heap<Integer> heap=new Heap<>((o1, o2) -> o1-o2);
        int []arr={1,2,6,7,2,4,8,1};
        //构建堆结构,⚠️堆结构直接打印还是无序 应该使用pop一次弹出才有序
        for (int i = arr.length-1; i>=0; i--) {
            heap.arrayHeapIfy(arr,i,arr.length);
        }
        //根据堆->做堆排序
        int heapSize=arr.length;
        while (heapSize>0){
        heap.swap(arr,0,--heapSize);//大顶堆,每次把最大的放到最后,再对新的堆做调整,排序结果由小到大
        heap.arrayHeapIfy(arr,0,heapSize);
        }
        System.out.println(Arrays.toString(arr));
    }
}

计数排序

/**基于非比较排序 计数排序
 *  对于给定范围的一些数 这个范围不能太大 创建help 数组从 【rangeMin rangeMax】
 *遍历原数组 每遇到一个数,在 help数组上+1 然后基于help数组再排序
 */
public static void countSort(int[] arr,int rangeMin,int rangeMax){
    int[] help=new int[rangeMax-rangeMin+1];
    for (int i = 0; i < help.length; i++) {
        help[i]=0;
    }
    for (int i = 0; i < arr.length; i++) {
        help[arr[i]-rangeMin]++;
    }
    int i=0;
    int j=0;
   while(i<arr.length && j<help.length){
       while ((help[j])>0){
           arr[i++]=j;
           help[j]--;
       }
       j++;
   }
}