排序算法整理

首先需要声明的是,本文并不会探讨每个算法内部的逻辑,而主要以代码实现和结论为主,需要了解具体原理的同学可以参考《算法导论》一书。

从大的方向上来说,排序算法可以分为内部排序和外部排序两种,内部排序 指的是整个排序过程不需要借助于外部存储器(比如磁盘),所有排序操作都可以在内存中完成。而如果参与排序的数据元素非常多,数据量非常大,则无法把整个排序过程放在内存中完成,必须借助于外部存储器(比如磁盘),这种排序算法又称为 **外部排序 **。

内部排序如果再进一步细分的话,又可以划分为下图中的一些算法。

内部排序分类

直接选择排序

  • 时间复杂度:O(n^2)
  • 空间复杂度:O(1)
  • 稳定性:不稳定
public static void sort(int[] arr) {
    for (int i = 0; i < arr.length - 2; i++) {
        int minIdx = i;
        for (int j = i + 1; j < arr.length; j++) {
            if (arr[j] < arr[minIdx]) {
                minIdx = j;
            }
        }
        if (minIdx != i) {
            Common.swap(arr, i, minIdx);
        }
    }
}

堆排序

  • 时间复杂度:O(nlogn)
  • 空间复杂度:O(1)
  • 稳定性:不稳定
public static void sort(int[] arr) {
    for (int i = arr.length - 1; i > 0; i--) {
        buildMaxHeap(arr, i);
        Common.swap(arr, 0, i);
    }
}

private static void buildMaxHeap(int[] arr, int end) {
    if (end > 0) {
        int firstNoneLeaf = (end - 1) / 2;
        for (int j = firstNoneLeaf; j >= 0; j--) {
            maxHeapify(arr, j, end);
        }
    }
}

private static void maxHeapify(int[] arr, int i, int end) {
    int leftChild = (i << 1) + 1;
    int rightChild = leftChild + 1;
    int maxIdx = i;
    if (leftChild <= end && arr[leftChild] > arr[maxIdx]) {
        maxIdx = leftChild;
    }
    if (rightChild <= end && arr[rightChild] > arr[maxIdx]) {
        maxIdx = rightChild;
    }
    if (maxIdx != i) {
        Common.swap(arr, i, maxIdx);
        maxHeapify(arr, maxIdx, end);
    }
}

归并排序

  • 时间复杂度:O(nlogn)
  • 空间复杂度:O(n)
  • 稳定性:稳定
public static void sort(int[] arr) {
    int size = arr.length;
    divide(arr, 0, size - 1);
}

private static void divide(int[] arr, int left, int right) {
    if (left < right) {
        int center = left + (right - left) / 2;
        sub_sort(arr, left, center);
        sub_sort(arr, center + 1, right);
        merge(arr, left, center, center + 1, right);
    }
}

private static void merge(int[] oriArr, int left1, int right1, int left2, int right2) {
    int[] assistArr = new int[oriArr.length];
    int pointer1 = left1;
    int pointer2 = left2;
    int tmp = left1;
    while (pointer1 <= right1 && pointer2 <= right2) {
        if (oriArr[pointer1] < oriArr[pointer2]) {
            assistArr[tmp] = oriArr[pointer1];
            pointer1++;
        } else {
            assistArr[tmp] = oriArr[pointer2];
            pointer2++;
        }
        tmp++;
    }
    while (pointer1 <= right1) {
        assistArr[tmp] = oriArr[pointer1];
        tmp++;
        pointer1++;
    }
    while (pointer2 <= right2) {
        assistArr[tmp] = oriArr[pointer2];
        tmp++;
        pointer2++;
    }
    tmp = left1;
    while (tmp <= right2) {
        oriArr[tmp] = assistArr[tmp];
        tmp++;
    }
}

冒泡排序

  • 时间复杂度:最好的情况下是O(n),最坏情况下是O(n^2)
  • 空间复杂度:O(1)
  • 稳定性:稳定
public static void sort(int[] arr) {
    int size = arr.length;
    for (int i = 0; i < size - 1; i++) {
        for (int j = 0; j < size - 1 - i; j++) {
            if (arr[j] > arr[j + 1]) {
                Common.swap(arr, j, j + 1);
            }
        }
    }
}

快速排序

  • 时间复杂度:O(nlogn)
  • 空间复杂度:由于快排使用了递归,递归是需要栈的,因此空间复杂度为O(logn)
  • 稳定性:不稳定
public static void sort(int[] arr) {
    int size = arr.length;
    quickSort(arr, 0, size - 1);
}

private static void quickSort(int[] arr, int start, int end) {
    if (start >= end) {
        return;
    }

    int left = start + 1;
    int right = end;
    int pivot = arr[start];

    // 当start + 1 == end时,也要进while一次,否则下面直接swap会出错,所以这里必须是<=
    while (left <= right) {
        // 从左侧开始找到第一个大于pivot的值,结果是要么找到,要么越界
        while (left <= end && arr[left] <= pivot) {
            left++;
        }
        // 从右侧开始找到第一个小于pivot的值,结果是要么找到,要么越界
        while (right >= start + 1 && arr[right] >= pivot) {
            right--;
        }

        // 这里left不可能等于right,如果left==right则意味着一个数既大于pivot又小于pivot,显然是矛盾的
        if (left < right) {
            Common.swap(arr, left, right);
        }
    }

    // 当right停在比pivot小的元素时,直接交换
    // 如果right越界,则right == start
    Common.swap(arr, start, right);
    subSort(arr, start, right - 1);
    subSort(arr, right + 1, end);
}

计数排序

/**
 * 计数排序
 * @param input 输入待排序数组
 * @param output 排序后的输出数组
 * @param k 输入数组的每个元素在[0, k]之间
 */
public static void sort(int[] input, int[] output, int k) {
    // 创建0-k的数组,并初始化为0
    int[] base = new int[k + 1];
    // 对于input中的每个元素,如果值i出现一次,则相应的base[i]加1
    // 最终base数组中每个位置的数值大小代表了取该值的元素数量
    for (int i = 0; i < input.length; i++) {
        base[input[i]] += 1;
    }
    // 因为base是已经排序好了的,所以从索引0开始,将当前索引处的值加到下一个索引的值上
    // 最后base数组每个元素的值大小,代表了有多少个元素小于等于当前索引的大小
    for (int i = 1; i <= k; i++) {
        base[i] += base[i - 1];
    }
    for (int i = 0; i < input.length; i++) {
        int index = base[input[i]];
        output[index - 1] = input[i];
        base[input[i]] -= 1;
    }
}

public static void sort2(int[] arr) {
    int min = arr[0];
    int max = arr[0];
    for (int i = 0; i < arr.length; i++) {
        int cur = arr[i];
        if (cur > max) {
            max = cur;
        }
        if (cur < min) {
            min = cur;
        }
    }

    int k = max - min + 1;
    int[] assist = new int[k];
    for (int i = 0; i < arr.length; i++) {
        assist[arr[i] - min] += 1;
    }

    for (int i = 0; i < k; i++) {
        // 当前位置元素数量
        int count = assist[i];
        if (i > 0) {
            assist[i] += assist[i - 1];
        }
        int curVal = i + min;
        int maxIdx = assist[i];
        if (count == 1) {
            arr[maxIdx - 1] = curVal;
        } else {
            for (int j = 0; j < count; j++) {
                arr[maxIdx - 1] = curVal;
                maxIdx--;
            }
        }
    }
}

基数排序

public static void lsdSort(int[] arr) {
    int size = arr.length;
    int maxVal = 0;
    for (int i = 0; i < size; i++) {
        if (arr[i] > maxVal) {
            maxVal = arr[i];
        }
    }

    int[][] assist = new int[10][size];
    int[] assistCount = new int[10];
    int digitPos = 1;
    while (maxVal / digitPos > 0) {
        for (int i = 0; i < size; i++) {
            int cur = arr[i];
            int radix = (cur / digitPos) % 10;
            assist[radix][assistCount[radix]] = cur;
            assistCount[radix]++;
        }

        int idx = 0;
        for (int i = 0; i < 10; i++) {
            int count = assistCount[i];
            if (count > 0) {
                for (int j = 0; j < count; j++) {
                    arr[idx] = assist[i][j];
                    idx++;
                }
                // assistCount 清零
                assistCount[i] = 0;
            }
        }

        digitPos *= 10;
    }
}

桶式排序

private static class Node {
    int data;
    Node next;

    public Node() {
    }

    public Node(int data) {
        this.data = data;
    }
}

public static void sort(int[] input, int[] output, int bucketSize) {
    Node[] bucket = new Node[bucketSize];
    for (int i = 0; i < bucket.length; i++) {
        bucket[i] = new Node(); // 头结点
    }
    for (int i = 0; i < input.length; i++) {
        int bucketIndex = hash(input[i]);
        Node node = new Node(input[i]);
        Node p = bucket[bucketIndex];
        if (p.next == null) { // 没有元素
            p.next = node;
        } else { // 已经有一个元素
            while (p.next != null && p.next.data <= node.data) {
                p = p.next;
            } // 跳出循环时候 该值小于下一个元
            node.next = p.next;
            p.next = node;
        }
    }
    int j = 0;
    for (int i = 0; i < bucket.length; i++) {
        for (Node p = bucket[i].next; p != null; p = p.next) {  // n/m
            output[j++] = p.data;
        }
    }
}

private static int hash(int value) {
    return value / 10;
}

直接插入排序

  • 时间复杂度:O(n^2)
  • 空间复杂度:O(1)
  • 稳定性:稳定
public static void sort(int[] arr) {
    for (int i = 1; i < arr.length; i++) {
        int tmp = arr[i];
        int j = i - 1;
        while (j >= 0 && arr[j] > tmp) {
            arr[j + 1] = arr[j];
            j--;
        }
        arr[j + 1] = tmp;
    }
}

折半插入排序

public static void sort(int[] arr) {
    for (int i = 1; i < arr.length; i++) {
        int tmp = arr[i];
        int index = binarySearch(arr, arr[i], i - 1);
        for (int j = i - 1; j >= index; j--) {
            arr[j + 1] = arr[j];
        }
        arr[index] = tmp;
    }
}

private static int binarySearch(int[] arr, int target, int maxIndex) {
    int find = 0;
    while (find <= maxIndex) {
        int mid = (find + maxIndex) / 2;
        if (target > arr[mid]) {
            find = mid + 1;
        } else {
            maxIndex = mid - 1;
        }
    }
    return find;
}

Shell排序

public static void sort(int[] a) {
    int n = a.length;
    int h  = 1;
    while (h < n / 3) {
        h = h * 3 + 1;
    }

    while (h >= 1) {
        for (int group = 0; group < h; group++) {
            subSort(a, group, h);
        }
        h = (h - 1) / 3;
    }
}

private static void subSort(int[] a, int start, int gap) {
    for (int i = start + gap; i < a.length; i += gap) {
        int item = a[i];
        int j = i;
        while (j > start && item < a[j - gap]) {
            a[j] = a[j - gap];
            j -= gap;
        }
        a[j] = item;
    }
}

完!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值