Back
Featured image of post 排序算法(一)

排序算法(一)

一、什么是排序算法

​ 所谓排序,就是使一串记录,按照其中的某个或某些关键字的大小,递增或递减的排列起来的。排序算法,就是如何使得记录按照要求排列的方法。排序算法在很多领域得到相当地重视,尤其是在大量数据的处理方面。一个优秀的算法可以节省大量的资源。在各个领域中考虑到数据的各种限制和规范,要得到一个符合实际的优秀算法,得经过大量的推理和分析。

二、排序的稳定性

在了解排序算法之前,我们先了解下排序的稳定性: 所谓的排序稳定性是指在排序的前后,元素的相对次序是否发生改变。若相对次序发生改变,那么证明该排序算法是不稳定的。 举个🌰,数组a的值为[1,5,3,5,4],我们可以看到排序前加粗的5排在前一个未加粗的5后面,经过某种排序算法后数组a的值为[1,3,4,5,5],此时加粗的5排在了未加粗的5前面,这样元素的相对次序就发生了改变,所以这种排序算法的稳定性就是不稳定的。

三、基础的排序算法

1.冒泡排序

​ 冒泡排序是一种简单的排序算法。它重复地走访过要排序的数列,一次比较两个元素,如果它们的顺序错误就把它们交换过来。走访数列的工作是重复地进行直到没有再需要交换,也就是说该数列已经排序完成。简单来说就是元素间两两相比较,当比对到数组中最后一个元素时,最大|最小的元素就落到数组中最后一个位置,再不断的重复此过程最终数组就变得有序了。

动图演示

(点击查看)

代码实现

先定义一个用于交换的方法,以便复用:

public static void swap(int[] nums,int i,int j){
       int temp=nums[i];
       nums[i]=nums[j];
       nums[j]=temp;
    }

冒泡排序:

//冒泡排序,时间复杂度O(n²),具有稳定性
    public static void bubbleSort(int[] nums){
        int len=nums.length-1;
        for (int i=len;i>0;i--){
            for (int j = 0; j <i ; j++) {
                if (nums[j]>nums[j+1]){
                  swap(nums,j,j+1);
                }
            }
        }
    }

冒泡排序的时间复杂度为O(n²),又因为再对比时相等的值不会交换,所以具有稳定性。

2.选择排序

算法描述

​ 选择排序是一种简单直观的排序算法,它也是一种交换排序算法,和冒泡排序有一定的相似度,可以认为选择排序是冒泡排序的一种改进。选择排序的原理就是:从未排序的数组中找到数组中的最大(小)值,放到未排序数组的起始位置,重复此过程最终整个数组就变成了一个有序数组

动图演示

(点击查看)

代码实现

//选择排序,时间复杂度O(n²),不具有稳定性
    public static void selectSort(int[] nums){
        int len=nums.length-1;
        for (int i = 0; i <=len ; i++) {
            int minIndex=i;
            for (int j = i+1; j <=len ; j++) {
                if (nums[j]<nums[minIndex]){
                    minIndex=j;
                }
            }
            swap(nums,i,minIndex);
        }
    }

​ 通过选择排序的代码我们可以发现,选择排序相比于冒泡排序减少了核心的交换次数,有着一定的优化。但总体来讲选择排序的时间复杂度总体来说也是O(n²),选择排序不具备排序的稳定性。

3.插入排序

算法描述

​ 插入排序是一种简单直观的排序算法。它的工作原理是通过构建有序序列,对于未排序数据,在已排序序列中从后向前扫描,找到相应位置并插入。简单来说,就是从左到右去维护一个稳定的区间,再不断的根据后续的元素插入到区间内的对应位置,最终数组整体的元素就变得有序了。

动图演示

(点击查看)

代码演示

//插入排序,时间复杂度O(n²),具备稳定性,且常数项最小
    public static void insertSort(int[] nums){
        int len=nums.length-1;
        for (int i = 1; i <=len; i++) {
            for (int j = i-1; j>=0 ; j--) {
                if (nums[j+1]<nums[j]){
                swap(nums,j+1,j);
                }
            }
        }
    }

虽然插入排序的时间复杂度也为O(n²),但它的常数项确实最小的,它也是冒泡排序的一种改良,我们可以发现这种算法减少了无用的比较和交换,当数据量小的时候常数项也决定着排序算法的效率。

如,在JDK 7 java.util.Arrays所用的sort方法的实现中,当待排数组长度小于47时,会使用插入排序。插入排序具备排序的稳定性。