经典排序算法

本文详细介绍了排序算法的基础概念,包括时间复杂度、空间复杂度以及稳定性,并列举了插入排序、选择排序、冒泡排序、快速排序、归并排序、基数排序、计数排序和堆排序的Java实现,帮助读者掌握这些基本的排序方法。
摘要由CSDN通过智能技术生成

排序算法

算法基础

1)时间复杂度

一个算法执行所消耗的时间。

2)空间复杂度

运行完一个算法所需的内存大小。

3)算法的稳定行

稳定排序:如果 a 原本在 b 的前面,且 a == b,排序之后 a 仍然在 b 的前面,则为稳定排序。

非稳定排序:如果 a 原本在 b 的前面,且 a == b,排序之后 a 可能不在 b 的前面,则为非稳定排序。

2、经典排序算法

2.1、插入排序-insertion

从数组第一个开始往后挑出一个数字,然后网这个数字位置前面去找,直到找到比它小的停止

package sort;

/**
 * @author zll
 * @description 插入排序
 */
public class SortInsert {

    public static void insertSort(int[] arr) {
        if (arr == null || arr.length <= 1) {
            return ;
        }

        for (int i = 1; i < arr.length; i++) {
            for (int j = i - 1; j >= 0; j--) {
                if (arr[j + 1] < arr[j]) {
                    Sort.swap(arr, j + 1, j);
                }
                else {
                    break;
                }
            }
        }
    }

    public static void insertSort2(int[] arr) {
        if (arr == null || arr.length <= 1) {
            return ;
        }

        for (int i = 1; i < arr.length; i++) {
            int j = i - 1;
            while (j >= 0 && arr[j + 1] < arr[j]) {
                Sort.swap(arr, j + 1, j--);
            }
        }
    }
}

2.2、选择排序-selection
package sort;

/**
 * @author zll
 * @description 选择排序
 */
public class SortSelection {

    public static void selectSort(int[] arr) {
        if (arr == null || arr.length < 2) {
            return ;
        }

        int len = arr.length;

        for (int i = 0; i < len - 1; i++) {
            int minIndex = i;
            for (int j = i + 1; j < len; j++) {
                if (arr[minIndex] > arr[j]) {
                    minIndex = j;
                }
            }
            Sort.swap(arr, i, minIndex);
        }
    }

    public static void selectSort2(int[] arr) {
        if (arr == null || arr.length < 2) {
            return ;
        }

        int len = arr.length;
        for (int i = 0; i < len - 1; i++) {
            int minIndex = i;
            int j = i + 1;
            while (j < len) {
                if (arr[minIndex] > arr[j]) {
                    minIndex = j;
                }
                j++;
            }
            Sort.swap(arr, i, minIndex);
        }
    }

    public static void selectSort3(int[] arr) {
        if (arr == null || arr.length < 2) {
            return ;
        }
        for (int i = 0; i < arr.length; i++) {

            for (int j = 0; j < i; j++) {
                if (arr[j] > arr[i]) {
                    Sort.swap(arr, i, j);
                }
            }
        }
    }

    public static void selectSort4(int[] arr) {
        if (arr == null || arr.length < 2) {
            return ;
        }
        for (int i = 0; i < arr.length; i++) {

            int j = 0;
            while (j < i) {
                if (arr[j] > arr[i]) {
                    Sort.swap(arr, i, j);
                }
                j ++;
            }
        }
    }
}
2.3、冒泡排序-bubble
package sort;

/**
 * @author zll
 * @description 冒泡排序
 */
public class SortBubble {


    public static void bubbleSort(int[] arr) {
        if (arr == null || arr.length <= 1) {
            return ;
        }

        for (int i = 0; i < arr.length; i++) {
            for (int j = 0; j < arr.length - i - 1; j++) {
                if (arr[j] > arr[j + 1]) {
                    Sort.swap(arr, j, j + 1);
                }
            }
        }
    }

    public static void bubbleSort2(int[] arr) {
        if (arr == null || arr.length <= 1) {
            return ;
        }

        for (int i = arr.length - 1; i > 0; i--) {
            for (int j = 0; j < i; j++) {
                if (arr[j] > arr[j + 1]) {
                    Sort.swap(arr, j, j + 1);
                }
            }
        }
    }
}
2.4、快速排序-quick
package sort;

import java.util.Stack;

/**
 * @author zll
 * @description 快速排序
 * 核心就是partition
 *
 * 1.0 找出arr中值比最右边位置R的数小的位置, 分成小于等于arr[R]和大于arr[R]的数据
 * 2.0 arr中值和最右边位置R的数比较, 分成小于arr[R], 等于 arr[R] 和大于arr[R]的数据三部分
 * 3.0 arr中值和最右边位置R的数比较, 分成小于arr[R](随机在L和R之间找一个数跟arr[R]交换), 等于 arr[R] 和大于arr[R]
 */
public class SortQuick {

    /**
     * 1.0
     */
    public static void sort1(int[] arr) {
        if (arr == null || arr.length <= 1) {
            return;
        }

        process1(arr, 0, arr.length - 1);
    }


    private static void process1(int[] arr, int L, int R) {
        if (L >= R) {
            return ;
        }
        // 找出中心值
        int M = partition1(arr, L, R);
        process1(arr, L, M - 1);
        process1(arr, M + 1, R);
    }

    /**
     * 1.0 找出arr中值比最右边位置R的数小的位置, 分成小于等于arr[R]和大于arr[R]的数据
     */
    private static int partition1(int[] arr, int L, int R) {
        if (L >= R) {
            return L > R ? -1 : L;
        }
        int lesserLeft = L - 1;
        int index = L;
        while (index < R) {
            if (arr[index] <= arr[R]) {
                Sort.swap(arr, index, ++lesserLeft);
            }
            index ++;
        }
        Sort.swap(arr, R, ++lesserLeft);
        return lesserLeft;
    }

    /**
     * 2.0
     */
    public static void sort2(int[] arr) {
        if (arr == null || arr.length <= 1) {
            return;
        }

        process2(arr, 0, arr.length - 1);
    }

    private static void process2(int[] arr, int L, int R) {
        if (L >= R) {
            return ;
        }
        // 等于R的左右边界
        int[] p = partition2(arr, L, R);
        process1(arr, L, p[0] - 1);
        process1(arr, p[1] + 1, R);
    }

    /**
     * 2.0 arr中值和最右边位置R的数比较, 分成小于arr[R], 等于 arr[R] 和大于arr[R]的数据三部分
     */
    private static int[] partition2(int[] arr, int L, int R) {
        if (L >= R) {
            return new int[]{-1, -1};
        }
        int lesserLeft = L - 1;
        int rightMore = R;
        int index = L;
        while (index < rightMore) {
            if (arr[index] < arr[R]) {
                Sort.swap(arr, index++, ++lesserLeft);
            } else if (arr[index] > arr[R]) {
                Sort.swap(arr, index, --rightMore);
            } else {
                index ++;
            }
        }
        // 交换最后一个相等的数
        Sort.swap(arr, R, rightMore);
        return new int[]{++lesserLeft, rightMore};
    }

    /**
     * 3.0
     */
    public static void sort3(int[] arr) {
        if (arr == null || arr.length <= 1) {
            return;
        }

        process3(arr, 0, arr.length - 1);
    }

    private static void process3(int[] arr, int L, int R) {
        if (L >= R) {
            return ;
        }
        /**
         * 3.0 arr中值和最右边位置R的数比较, 分成小于arr[R](随机在L和R之间找一个数跟arr[R]交换), 等于 arr[R] 和大于arr[R]
         */
        // 随机交换L~R之间的位置跟R的数据(防止逆序 情况下的极端问题)
        Sort.swap(arr, L + (int) (Math.random() * (R - L + 1)), R);
        // 等于R的左右边界
        int[] p = partition2(arr, L, R);
        process1(arr, L, p[0] - 1);
        process1(arr, p[1] + 1, R);
    }
 

    /**
     * 非递归方式实现快速排序
     */
    public static void sortNotRecursive(int[] arr) {
        if (arr == null || arr.length <= 1) {
            return;
        }

        Stack<int[]> stack = new Stack<>();
        stack.add(new int[]{0, arr.length - 1});
        while (!stack.isEmpty()) {
            int[] pop = stack.pop();
            if (pop[0] < pop[1]) {
                // 随机交换最右边数值
                Sort.swap(arr, pop[0] + (int)(Math.random() * (pop[1] - pop[0] + 1)), pop[1]);
                int[] p = partition2(arr, pop[0], pop[1]);
                stack.add(new int[]{pop[0], p[0] - 1});
                stack.add(new int[]{p[1] + 1, pop[1]});
            }
        }
    }
}
2.5、归并排序-merge
package sort;

/**
 * @author zll
 * @description 每次分一半为左右, 左右继续分一半, 直至最小粒度,比较大小成有序, 开始合并左右成为有序,直至最后有序
 */
public class SortMerge {

    public static void sort(int[] arr) {
        if (arr == null || arr.length <= 1) {
            return;
        }

        process(arr, 0, arr.length -1);
    }

    private static void process(int[] arr, int L, int R) {
        if (L >= R) {
            return;
        }
        int mid = L + ((R - L) >> 2);

        process(arr, L, mid);
        process(arr, mid+1, R);

        merge(arr, L, mid, R);
    }

    /**
     * 合并数组两边,按照大小
     * @param arr 数组
     * @param from 左
     * @param mid 中
     * @param to 右
     */
    private static void merge(int[] arr, int from, int mid, int to) {
        int[] tempArr = new int[to - from + 1];
        int L = from, R = mid + 1;
        int index = 0;
        // 比较左右数组大小
        while (L <= mid && R <= to) {
            tempArr[index++] = arr[L] > arr[R] ? arr[R++] : arr[L++];
        }

        // 左边还剩余
        while (L <= mid) {
            tempArr[index++] = arr[L++];
        }

        // 右边还剩余
        while (R <= to) {
            tempArr[index++] = arr[R++];
        }

        // 替换原始数组
        for (int i = 0; i < tempArr.length; i++) {
            arr[from + i] = tempArr[i];
        }
    }

    public static void sortNotRecursive(int[] arr) {
        if (arr == null || arr.length <= 1) {
            return;
        }
        int step = 1;
        int N = arr.length;
        while (step < N) {
            int L = 0;
            while (L < N) {
                // 取得中间值(-1是要把L位置算上)
                int M = L + step - 1;
                if (M >= N - 1) {
                    break;
                }

                // 右边界
                int R = M + step;
                if (R > N - 1) {
                    R = N - 1;
                }

                merge(arr, L, M, R);

                // 跳转下一组
                L = R + 1;
            }

            // 2的倍数增长
            step *= 2;
        }
    }
}
2.6、基数排序-radix
/**
 * @author zll
 * @description 基数排序 - 基于桶排序
 * 基于 0 ~ 9 - 10个桶
 * 从低位 -> 高位的数字依次进桶(队列) (从右向左进桶)
 * 从低位比较,到高位比较
 */
public class RadixSort {

    /**
     * 排序
     * @param arr
     */
    public static void sort(int[] arr) {
        if (arr == null || arr.length <= 1) {
            return ;
        }
        int length = arr.length;

        //思路:
        // 1.取得最大数据的位数
        int d = maxBits(arr);
        int radix = 10;
        int[] help = new int[length];
        for (int i = 1; i <= d; i++) {
            // 2.从左往右从个位到高位,把数据累计到0~9的数组中C
            int[] count = new int[radix];
            for (int j = 0; j < length; j++) {
                int digit = getDigit(arr[j], i);
                count[digit]++;
            }
            // 3.然后计算0~9的数组的累加和C,保证值小的获得小的索引
            for (int k = 1; k < radix; k++) {
                count[k] = count[k] + count[k - 1];
            }
            // 4.然后数组从右往左,个位到高位的数据落入到0~9数组A中
            // 5.取得上一步桶中的数值x,即是<=x的数有多少个,然后把最右边取得得数放入0~x下标的数组中x位置,相应的C累计和-1
            for (int m = length - 1; m >= 0 ; m--) {
                int digit = getDigit(arr[m], i);
                int x = count[digit];
                if (x > 0) {
                    // 获取出去排序的值
                    help[x - 1] = arr[m];
                    // 索引被占删除
                    count[digit]--;
                }
            }
            // 排好序的写回原始数据
            for (int j = 0; j < help.length; j++) {
                arr[j] = help[j];
            }
        }
    }

    /**
     * 数组中数值最大的位数
     * @param arr 数组
     */
    private static int maxBits(int[] arr) {
        int max = Integer.MIN_VALUE;
        for (int i = 0; i < arr.length; i++) {
            max = Math.max(max, arr[i]);
        }
        int res = 0;
        while (max != 0) {
            max /= 10;
            res++;
        }
        return res;
    }

    /**
     * 获取数字x第d位数值
     * @param x 数值
     * @param d 位数
     * @return int
     */
    private static int getDigit(int x, int d) {
        return ((x / ((int) Math.pow(10, d - 1))) % 10);
    }


    public static void main(String[] args) {
        int testCount = 100000;
        int maxVal = 200;
        int maxSize = 200;
        for (int i = 0; i < testCount; i++) {
            int[] arr = ArrayUtil.randomPositiveArr(maxSize, maxVal);
            int[] arr1 = ArrayUtil.copyArr(arr);
            sort(arr);
            Arrays.sort(arr1);
            boolean equal = ArrayUtil.isEqual(arr, arr1);
            if (!equal) {
                System.out.println("Opos!");
                break;
            }
        }

        System.out.println("finished!!");
    }
}

2.7、计数排序-count
/**
 * @author zll
 * @description 计数排序 - 基于桶的排序
 * 基于样本, 比较少的类型, 比如年龄,可以细化到 0-200岁, 创建201个桶
 */
public class CountSort {

    /**
     * only for 0~200 value, 超过200也不会报错,但是效率会变低
     * @param arr
     */
    public static void sort(int[] arr) {
        if (arr == null || arr.length < 2) {
            return ;
        }
        // 找出arr的最大值 / 直接创建201个桶,省去数组循环的次数(这个就只适用于0-200了)
        int max = Integer.MIN_VALUE;
        int length = arr.length;
        for (int i = 0; i < length; i++) {
            max = Math.max(max, arr[i]);
        }
        int[] bucket = new int[max + 1];
        for (int i = 0; i < length; i++) {
            bucket[arr[i]]++;
        }
        int j = 0;
        for (int i = 0; i < bucket.length; i++) {
            while (bucket[i]-- > 0) {
                arr[j++] = i;
            }
        }
    }

    public static void main(String[] args) {
        int testCount = 100000;
        int maxVal = 200;
        int maxSize = 200;
        for (int i = 0; i < testCount; i++) {
            int[] arr = ArrayUtil.randomPositiveArr(maxSize, maxVal);
            int[] arr1 = ArrayUtil.copyArr(arr);
            sort(arr);
            Sorted.selectSort(arr1);
            boolean equal = ArrayUtil.isEqual(arr, arr1);
            if (!equal) {
                System.out.println("Opos!");
                break;
            }
        }

        System.out.println("finished!!");
    }
}
2.8、堆排序
package com.algorithm.algorithm.basic.sort;

import com.algorithm.algorithm.utils.ArrayUtil;

import java.util.Arrays;

/**
 * @author zll
 * @description 堆的排序
 */
public class HeapSort {

    /**
     * 堆排序
     * @param arr
     */
    public static void sort(int[] arr) {
        if (arr == null || arr.length < 2) {
            return ;
        }

        // 首先做成大顶堆,这个一般一下数据
        for (int i = arr.length - 1; i >= 0;i--) {
            heapify(arr, i, arr.length);
        }

        int heapSize = arr.length;
        // 把最大值交换到数组最末
        ArrayUtil.swap(arr, 0, --heapSize);
        // O(N*logN)
        while (heapSize > 0) { // O(N)
            heapify(arr, 0, heapSize); // O(logN)
            // 继续交换最大数字
            ArrayUtil.swap(arr, 0, --heapSize); // O(1)
        }
    }

    /**
     * 往下沉, 从下往上一次不断找自己下面的孩子是否比自己大
     * @param arr
     * @param index
     * @param heapSize
     */
    public static void heapify(int[] arr, int index, int heapSize) {
        // 从下往上一次不断找自己下面的孩子是否比自己大
        int leftIndex = index * 2 + 1;
        while (leftIndex < heapSize) {
            // 找出来右孩子
            int rightIndex = leftIndex + 1;
            // 如果右孩子存在并且既大于左孩子又大于index值则取右孩子的下标, 否则如果左孩子大于index值则取左孩子下标, 否还是index
            int largeIndex = rightIndex < heapSize &&
                    arr[rightIndex] > arr[leftIndex] &&
                    arr[rightIndex] > arr[index] ?
                        rightIndex :
                        (arr[leftIndex] > arr[index] ? leftIndex : index);

            if (largeIndex == index) {
                break;
            }
            ArrayUtil.swap(arr, index, largeIndex);
            index = largeIndex;
            leftIndex = index * 2 + 1;
        }
    }

    public static void main(String[] args) {
        System.out.println("开始");
        for (int i = 0; i < 500000; i++) {
            int[] arr = ArrayUtil.randomArr(100, 1000);
            int[] arr1 = ArrayUtil.copyArr(arr);
            Arrays.sort(arr);
            sort(arr1);
            if (!ArrayUtil.isEqual(arr, arr1)) {
                System.out.println("错误了");
                ArrayUtil.printArr(arr);
                ArrayUtil.printArr(arr1);
                break;
            }
        }
        System.out.println("结束");
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值