大话数据结构之排序算法

818 阅读3分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第9天,点击查看活动详情

算法介绍

算法时间复杂度,一般通过 O()来体现,我们称为大 O 记法。

  1. 常数阶

  2. 线性阶

  3. 对数阶

  4. 平方阶

    举例:

十大排序算法

  1. 冒泡排序

算法描述:

  • 比较相邻的两个元素,如果第一个比第二个大,就交换
  • 对每一对相邻的元素作同样的工作
  • 针对所有元素做重复的步骤
 /**
     * 冒泡排序
     * @param arr
     */
    public void bubbleSort(Integer[] arr){
        for (int j=0;j<arr.length;j++){
            for (int i=0;i<arr.length-1;i++){
                Integer temp=arr[i];
                if(arr[i]>arr[i+1]){
                    arr[i]=arr[i+1];
                    arr[i+1]=temp;
                }
            }
        }
        System.out.println(Arrays.asList(arr));
    }

时间复杂度:O(n^2) 空间复杂度:O(1) 稳定度:稳定

  1. 选择排序

算法描述:

  • 先在未排序队列中找到最小的元素放到排序序列的起始位置
  • 再在剩下的未排序的里面找最小的放在排序序列的末尾
  • 依此类推,直到所有所有元素排序完毕
 public void selectSort(Integer[] arr){
        for (int i=0;i<arr.length-1;i++){
            Integer minIndex=i;
            for (int j=i+1;j<arr.length;j++){
                if(arr[j]<arr[minIndex]){
                    minIndex=j;
                }
            }
            Integer temp=arr[minIndex];
            arr[minIndex]=arr[i];
            arr[i]=temp;
        }
        System.out.println(Arrays.asList(arr));
    }

时间复杂度:O(n^2) 空间复杂度:O(1) 稳定度:稳定

  1. 插入排序

算法描述:

  • 从第一个元素开始,该元素认为是已排序的
  • 取出下一个元素 A,在已排序的元素序列中从后向前扫描,如果已排序的元素比 A 大,那他们就后移一位
  • 直到找到已排序的元素小于或者等于 A
  • 插入该位置

详细:从集合里第二个元素开始,跟他前面的元素比较,如果比他大就不动,比他小,就让他后移一位。都移动完毕后,将这个值插入。

 public void insertSort(Integer[] arr){
        for (int i=0;i<arr.length-1;i++){
            Integer next=arr[i+1];
            int preIndex=i;
            while (preIndex>=0&&next<arr[preIndex]){
                arr[i+1]=arr[i];
                preIndex--;
            }
            arr[preIndex+1]=next;
        }
        System.out.println(Arrays.asList(arr));
    }
  1. 希尔排序

算法描述: 将大的数据集合根据增量分为若干个小组,然后对每个小组分别进行插入排序。对每个分组排序后,每个分组自身就是有序的了。然后缩小增量,再进行插入排序。直到全部排序完成

public void shellSort(Integer []array){
        int j;
        for (int gap=array.length/2;gap>0;gap=gap/2){
           for (int i=gap;i<array.length;i++){
               Integer temp=array[i];
               for (j=i;j>=gap&&temp<array[j-gap];j-=gap){
                   array[j]=array[j-gap];
               }
               array[j]=temp;
           }
        }
        System.out.println(Arrays.asList(array));

    }
  1. 堆排序

算法描述:

  • 将数组进行建堆,一次建堆完成后,将堆顶最大值放在数组尾部。
  • 将剩下的元素再建堆,再交换
public void heapSort(Integer []array){
        for (int i = 0; i < array.length; i++) {

            //每次建堆就可以排除一个元素了
            maxHeapify(array, array.length - i);

            //交换
            int temp = array[0];
            array[0] = array[(array.length - 1) - i];
            array[(array.length - 1) - i] = temp;

        }
        System.out.println(Arrays.asList(array));
    }

    public  void maxHeapify(Integer[] arrays, int size) {

        // 从数组的尾部开始,直到第一个元素(角标为0)
        for (int i = size - 1; i >= 0; i--) {
            heapify(arrays, i, size);
        }
    }

    private void heapify(Integer []array,int currentRootNode,int size){
        int leftChild=currentRootNode*2+1;
        int rightChild=currentRootNode*2+2;
        int maxIndex=currentRootNode;
        if(leftChild<size){
            if(array[leftChild]>array[currentRootNode]){
                maxIndex=leftChild;
            }
        }
        if(rightChild<size){
            if(array[rightChild]>array[currentRootNode]){
                maxIndex=rightChild;
            }
        }
        if(maxIndex!=currentRootNode){
            Integer temp = array[currentRootNode];
            array[currentRootNode]=array[maxIndex];
            array[maxIndex]=temp;

        heapify(array, currentRootNode,size);
        }
    }
  1. 归并排序

算法描述:典型的分治策略,将一个数组拆分开,分别排序后再合并

 public void mergeSort(Integer []array){
        mergeSort(array,0,array.length-1);
        System.out.println(Arrays.asList(array));
    }

    public void mergeSort(Integer []array,int left,int right){
        if(left<right){
            int center=  left + ((right - left) >> 1);
            mergeSort(array,left,center);
            mergeSort(array,center+1,right);
            merge(array,left,center,right);
        }
    }

    public void merge(Integer []array,int left,int center,int right){
        int[] temp = new int[right - left + 1];
        int i = 0;
        int p1 = left;
        int p2 = center + 1;
        // 比较左右两部分的元素,哪个小,把那个元素填入temp中
        while(p1 <= center && p2 <= right) {
            temp[i++] = array[p1] < array[p2] ? array[p1++] : array[p2++];
        }
        // 上面的循环退出后,把剩余的元素依次填入到temp中
        // 以下两个while只有一个会执行
        while(p1 <= center) {
            temp[i++] = array[p1++];
        }
        while(p2 <= right) {
            temp[i++] = array[p2++];
        }
        // 把最终的排序的结果复制给原数组
        for(i = 0; i < temp.length; i++) {
            array[left + i] = temp[i];
        }
    }

时间复杂度:O(nlogn)
空间复杂度:O(N)
稳定度:稳定的排序算法

  1. 快速排序

算法描述:

  • 先从数列中取出一个数作为基准数
  • 分区过程,将比这个数大的数全放到它的右边,小于或等于它的数全放到它的左边
  • 再对左右区间重复第二步,直到各区间只有一个数
 public void quickSort(Integer[] array){
        if(array==null||array.length<2){
            return;
        }
        quickSort(array,0,array.length-1);
        System.out.println(Arrays.asList(array));
    }

    public  void quickSort(Integer[] array, int left, int right) {
        if(left < right) {
            // 把数组中随机的一个元素与最后一个元素交换,这样以最后一个元素作为基准值实际上就是以数组中随机的一个元素作为基准值
            swap(array, new Random().nextInt(right - left + 1) + left, right);
            Integer[] p = partition(array, left, right);
            quickSort(array, left, p[0] - 1);
            quickSort(array, p[1] + 1, right);
        }
    }

    Integer[] partition(Integer[] array,int left,int right){
        Integer basic=array[right];
        int less=left-1;
        int more=right+1;
        while (left<more) {
            if(array[left]<basic){
                swap(array,++less,left++);
            }else if(array[left]>basic){
                swap(array,--more,left);
            }else {
                left++;
            }
        }
        return new Integer[] { less + 1, more - 1 };
    }

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

时间复杂度:O(nlogn)
空间复杂度:O(logn)
稳定性:不稳定

  1. 计数排序

特点:每个桶存单一键值

算法描述:

  • 得到无序数组的取值范围
  • 根据取值范围创建对应数量的桶
  • 遍历数组,将每个元素放入桶中
  • 按照顺序遍历桶中的每个元素,依次放到数组中,即可完成数组的排序。

时间复杂度:O(n)
空间复杂度:取决于元素的取值范围,总的来说为 O(n)
稳定性:稳定

public void bucketSort(Integer[] array){
        if (array == null || array.length < 2) {
            return;
        }

        int max = Integer.MIN_VALUE;

        for (int i = 0; i < array.length; i++) {
            max = Math.max(max, array[i]);
        }

        int[] bucket=new int[max+1];

        for (int i=0;i<array.length;i++){
            bucket[array[i]]++;
        }

        int i=0;
        for (int j = 0; j < bucket.length; j++) {
            while (bucket[j]-- > 0) {
                array[i++] = j;
            }
        }
        System.out.println(Arrays.asList(array));
    }
  1. 基数排序

如果其中有一个数很大,计数排序就不好使了,但是我们有一个新思路,就是分位存储。通过拆分个位,十位,百位的方法来做。

算法描述:

  • 先算出最大值,看看是几位数
  • 取出个位数放入桶中,在取出来放入原数组,此时个位已经排好序了
  • 取出十位数放入桶中,在取出来放入原数组,此时十位个位已经排好序了
  • 取出百位数放入桶中。。。
  • 依次类推