基础排序算法整理(java实现了一小部分)

一、算法分类

常见排序算法可以分为两大类:

非线性时间比较类排序:通过比较来决定元素间的相对次序,由于其时间复杂度不能突破O(nlogn),因此称为非线性时间比较类排序。

线性时间非比较类排序:不通过比较来决定元素间的相对次序,它可以突破基于比较排序的时间下界,以线性时间运行,因此称为线性时间非比较类排序。 

相关概念:

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

不稳定:如果a原本在b的前面,而a=b,排序之后 a 可能会出现在 b 的后面。

时间复杂度:对排序数据的总的操作次数。反映当n变化时,操作次数呈现什么规律。

空间复杂度:是指算法在计算机内执行时所需存储空间的度量,它也是数据规模n的函数。 

1、计数排序 —— 基础桶排序(时间快,空间大)

计数排序(Counting Sort),工作的原理是将数组分到有限数量的桶子里。

这个算法就好比有11个桶,编号从0~10。每出现一个数,就将对应编号的桶中的放一个小旗子,最后只要数数每个桶中有几个小旗子就OK了。例如2号桶中有1个小旗子,表示2出现了一次;3号桶中有1个小旗子,表示3出现了一次;5号桶中有2个小旗子,表示5出现了两次;8号桶中有1个小旗子,表示8出现了一次。

代码实现如下:

    /**
     * 桶排序:浪费空间
     */
    @Test
    public void bucketSorting() {
        int[] bucketNums = new int[10000];
        int[] nums = new int[]{17, 200, 1, 5000, 700, 5000, 9999};
        List resultNums = new ArrayList();
        for (int num : nums) {
            bucketNums[num] = bucketNums[num] + 1;
        }
        for (int i = 0; i < bucketNums.length; i++) {
            if (bucketNums[i] > 0) {
                while (--bucketNums[i] >= 0) {
                    resultNums.add(i);
                }
            }
        }
        System.out.println(resultNums);
    }

根据代码,我们用大写字母O来表示时间复杂度,M为桶的个数,N为待排序数的个数,最终桶排序的时间复杂度为O(M+N)。

2、冒泡排序(节约空间,浪费时间)

冒泡排序(Bubble Sort),它重复地走访过要排序的元素列,依次比较两个相邻的元素,如果他们的顺序错误就把他们交换过来。走访元素的工作是重复地进行直到没有相邻元素需要交换,也就是说该元素已经排序完成。

代码实现如下:

    /**
     * 冒泡排序:浪费时间
     */
    @Test
    public void bubbleSort() {
        int[] nums = new int[]{17, 200, 1, 5000, 700, 5000, 9999};
        int num;
        for (int i = 0; i < nums.length; i++) {
            for (int j = i + 1; j < nums.length - i; j++) {
                if (nums[i] > nums[j]) {
                    num = nums[j];
                    nums[j] = nums[i];
                    nums[i] = num;
                }
            }
        }
        System.out.println(Arrays.toString(nums));
    }

根据代码,我们用大写字母O来表示时间复杂度,N为待排序数的个数,最终冒泡排序的时间复杂度为O(N^2)。

3、快速排序(时间与空间平衡)

快速排序(Quicksort)是对冒泡排序的一种改进。

它的基本思想是:通过一趟排序将要排序的数据分割成独立的两部分,其中一部分的所有数据都比另外一部分的所有数据都要小,然后再按此方法对这两部分数据分别进行快速排序,整个排序过程可以递归进行,以此达到整个数据变成有序序列。

代码实现如下:

    /**
     * @param arr 排序集合
     * @param low 开始位
     * @param high 结束位
     */
    private void quickSort(int[] arr, int low, int high) {
        int i = low;
        int j = high;
        int t;
        if (low > high) {
            return;
        }
        // temp就是基准位
        int temp = arr[low];

        while (i < j) {
            // 先看右边,依次往左递减
            while (temp <= arr[j] && i < j) {
                j--;
            }
            // 再看左边,依次往右递增
            while (temp >= arr[i] && i < j) {
                i++;
            }
            // 如果满足条件则交换
            if (i < j) {
                t = arr[j];
                arr[j] = arr[i];
                arr[i] = t;
            }

        }
        // 最后将基准为与i和j相等位置的数字交换
        arr[low] = arr[i];
        arr[i] = temp;
        // 递归调用左半数组
        quickSort(arr, low, j - 1);
        // 递归调用右半数组
        quickSort(arr, j + 1, high);
    }

    /**
     * 快速排序
     */
    @Test
    public void quickSort() {
        int[] arr = {1, 7, 2, 4, 7, 62, 3, 4, 2, 10, 8, 9, 19};
        quickSort(arr, 0, arr.length - 1);
        System.out.println(Arrays.toString(arr));
    }

根据代码,我们用大写字母O来表示时间复杂度,N为待排序数的个数,最坏的情况就是冒泡排序的时间复杂度为O(N^2),每个都要判断一次,平均的时间复杂度为O(NlogN)。

4、选择排序(Selection Sort)

选择排序(Selection-sort)是一种简单直观的排序算法。它的工作原理:首先在未排序序列中找到最小(大)元素,存放到排序序列的起始位置,然后,再从剩余未排序元素中继续寻找最小(大)元素,然后放到已排序序列的末尾。以此类推,直到所有元素均排序完毕。 

public static void main(String[] args) {
    int[] arrs = {1, 7, 2, 4, 7, 62, 3, 4, 2, 10, 8, 9, 19};
    for(int i = 0; i < arrs.length; i++) {
        int min = arrs[i];
        int minIndex = i;
        for(int j = i; j < arrs.length; j++) {
            if(min > arrs[j]) {
                min = arrs[j];
                minIndex = j;
            }
        }
        if(minIndex != i) {
            arrs[minIndex] = arrs[i];
            arrs[i] = min;
        }
    }
    System.out.println(Arrays.toString(arrs));
}

5、插入排序(Insertion Sort)

插入排序(Insertion-Sort)的算法描述是一种简单直观的排序算法。它的工作原理是通过构建有序序列,对于未排序数据,在已排序序列中从后向前扫描,找到相应位置并插入。

public static void main(String[] args) {
    int[] arrs = {7, 2, 4, 7, 62, 3, 4, 2, 1, 10, 8, 9, 19};
    for(int i = 0; i < arrs.length - 1; i++) {
        int current = arrs[i+1];
        int index = i;
        while(index >= 0 && current < arrs[index]) {
            arrs[index + 1] = arrs[index];
            index--;
        }
        arrs[index+1] = current;
    }
    System.out.println(Arrays.toString(arrs));
}

6、归并排序(Merge Sort)

归并排序是建立在归并操作上的一种有效的排序算法。该算法是采用分治法(Divide and Conquer)的一个非常典型的应用。将已有序的子序列合并,得到完全有序的序列;即先使每个子序列有序,再使子序列段间有序。若将两个有序表合并成一个有序表,称为2-路归并。

7、桶排序(Bucket Sort)

桶排序是计数排序的升级版。它利用了函数的映射关系,高效与否的关键就在于这个映射函数的确定。桶排序 (Bucket sort)的工作的原理:假设输入数据服从均匀分布,将数据分到有限数量的桶里,每个桶再分别排序(有可能再使用别的排序算法或是以递归方式继续使用桶排序进行排)。

8、基数排序(Radix Sort)

基数排序是按照低位先排序,然后收集;再按照高位排序,然后再收集;依次类推,直到最高位。有时候有些属性是有优先级顺序的,先按低优先级排序,再按高优先级排序。最后的次序就是高优先级高的在前,高优先级相同的低优先级高的在前。

9、希尔排序(Shell Sort)

1959年Shell发明,第一个突破O(n2)的排序算法,是简单插入排序的改进版。它与插入排序的不同之处在于,它会优先比较距离较远的元素。希尔排序又叫缩小增量排序

10、堆排序(Heap Sort)

堆排序(Heapsort)是指利用堆这种数据结构所设计的一种排序算法。堆积是一个近似完全二叉树的结构,并同时满足堆积的性质:即子结点的键值或索引总是小于(或者大于)它的父节点。

图片文字引用博客:十大经典排序算法(动图演示)

猜你喜欢

转载自blog.csdn.net/zhangjian8641/article/details/86441959