七大排序算法总结

前言

本篇博客将根据现有知识对数据结构七大排序算法做以小结,以下博客仅作为个人学习过程的小结,如能对各位博友有所帮助不胜荣幸。
本篇博客将简单介绍七大排序算法的概念原理应用,以及各自优点及区别,只做本人小结,后期随学习深入再做补充修改。

七大排序算法总览

在这里插入图片描述

插入排序

原理

类似于插入扑克牌,将整个区间分为有序区间和无序区间两个,每次从无序区间中取出第一个元素,与有序区间中的元素逐一比较,插入到合适位置,重复以上操作直到无序区间元素被区完

实现

//初始时:
    //无序区间:[0,1)
    //有序区间:[1,arr.length-1]
    public static void insertSort(int[] arr){
        for(int i = 1; i < arr.length; i++){
            int val = arr[i];
            //无序区间中的第一个元素
            int j;
            for(j = i-1; j >= 0 && arr[j] > val; j--){
                //有序区间从后向前遍历
                arr[j+1] = arr[j];
                //有序区间中遍历到的该元素a[j]>判断值val,则该元素在数组中的位置向后移一位
            }
            arr[j+1] = val;
        }
    }

性能

时间复杂度:
最好:O(n) 最坏:O(n²) 平均:O(n²)
空间复杂度:
O(1)

优化:二分插入排序

因为插入排序总是在无序区间中找元素,然后遍历有序区间查找合适的位置插入,而因为区间有序,所以在有序区间查找位置时可以用二分查找的办法提高效率

希尔排序

原理

希尔排序是插入排序的优化,其基本思想为先选定一个整数 len,将整个区间分组,分组依据为,所有距离为 len 的元素在同一组,并对每组内部进行插入排序,然后减小len的值,重复以上操作,直到len == 1

实现

public static void shellSort(int[] arr){
        int len = arr.length;
        while(len > 1){
            gapInsertSort(arr,len);
            len = (len / 3) + 1;
        }
        gapInsertSort(arr,1);
    }
    private static void gapInsertSort(int[] arr,int len){
        for(int i = 1; i < arr.length; i++){
            int val = arr[i];
            int j = i-len;
            for(; j >= 0 && arr[j] > val; j-=len){
                arr[j+len] = arr[j];
            }
            arr[j+len] = val;
        }
    }

性能

时间复杂度:
最好:O(n) 最坏:O(n²) 平均:O(n^1.3)
空间复杂度:
O(1)

选择排序

原理

每次在无序端寻找最大(小)的元素,将其放到无序序区间的最后端(前端),即遍历找最值放到有序区间的两端,直到无序区间被遍历完

实现

public static void selectSort(int[] arr){
        //无序区间[0,array.length)
        //有序区间(array.length,array.length)
        for(int i = arr.length; i > 0; i--){
            int j = 0;
            int index = 0;
            for(; j < i ; j++){
                if(arr[j] > arr[index]){
                    index = j;
                }
            }
            int t = arr[index];
            arr[index] = arr[i-1];
            arr[i-1] = t;
        }
    }

性能

时间复杂度:最坏/最好/平均 O(n²)
空间复杂度:O(1)

优化:双向选择排序

因为选择排序总是要整体遍历一遍无序数组,在其中找到最值,则可以通过一次遍历同时找到最大值和最小值,分别放到无序区间的两端,这样就对选择排序提高了一半的效率

堆排序

原理

其基本思想和选择排序一样,也是每次在无序区间中寻找最值,放到两端,而不同的是堆排序中查找最值的方式是通过堆来选择最值
注意:升序排序建大堆,降序排序建小堆

实现

public static void heapSort(int[] arr){
        buildHeap(arr);
        for(int i = arr.length-1; i >= 0; i--){
            //交换前
            //无序数组[0,i]
            //有序数组(i,arr.length)
            swap(arr,i,0);
            shiftDown(arr,i,0);
            //交换后
            //无序数组[0,i-1]
            //有序数组(i-1,arr.length)
            //无序数组长度:i
        }
    }
    //建堆
    private static void buildHeap(int[] arr){
        int index = (arr.length-1-1)/2;
        for(int i = index; i >= 0; i--){
            shiftDown(arr,arr.length,i);
        }
    }
    //交换数组两元素
    private static void swap(int[] arr,int a,int b){
        int t = arr[a];
        arr[a] = arr[b];
        arr[b] = t;
    }
    //堆向下调整
    private static void shiftDown(int[] arr,int size,int index){
        int left = index * 2 + 1;
        while(left < size){
            int max = left;
            int right = left+1;
            if(right < size){
                if(arr[right] > arr[max]){
                    max = right;
                }
            }

            if(arr[index] >= arr[max]){
                break;
            }
            swap(arr,index,max);

            index = max;
            left = index * 2 + 1;
        }
    }

性能

时间复杂度:
O(n * log(n))
空间复杂度:
O(1)

冒泡排序

原理

无序区间中,逐次遍历两相邻元素比较,小的在前大的在后,每次将最大数冒泡到无序区间最后,重复以上操作,直到整体有序

实现

public static void bubbleSort(int[] arr){
        for(int i = arr.length-1; i >= 0; i--){
            boolean isSort = true;
            //无序数组:[0,i)
            //有序数组:[i,arr.length)
            for(int j = 0; j < i; j++){
                if(arr[j] > arr[j+1]){
                    swap(arr,j,j+1);
                    isSort = false;
                }
            }
            if(isSort){
                break;
                //优化,如果无序区间遍历一次未有元素进行交换,则表明有序
            }
        }
    }

性能

时间复杂度:
最好:O(n) 最坏:O(n²) 平均:O(n²)
空间复杂度:
O(1)

快速排序

原理

  1. 在区间中选择一个数(val),作为基准下标为(index)
  2. 遍历整个区间,小于 val 的放 index 的左边,大于 val 的放 index 的右边,将整个区间分为三段 [0 , index) 、[index] 、(index , arr.length-1]
  3. 再对左右两区间进行如上操作,直到左右区间长度为1表示已有序,或长度为0表示无数据

实现

private static void quickSortFunction(int[] arr,int start,int end){
        if(start == end){
            return;
        }
        if(start > end){
            return;
        }
        //为方便起见,每次将index起始位置定为start
        int index = partition(arr,start,end);
        quickSortFunction(arr,start,index-1);
        //index 之前的都是比 基准值 小的
        quickSortFunction(arr,index+1,end);
        //index 之前的都是比 基准值 大的
    }

    private static int partition(int[] arr, int start, int end) {
        int left = start;
        int right = end;
        while (left < right){
            while (left < right && arr[left] <= arr[start]){
                left++;
            }
            while (left < right && arr[right] >= arr[start]){
                right--;
            }
            swap(arr,left,right);
        }
        swap(arr,start,right);
        return right;
    }

性能

时间复杂度:
最好:O(nlog(n)) 最坏:O(n²) 平均:O(nlog(n))
空间复杂度:
最好:O(log(n)) 最坏:O(n) 平均:O(log(n))

优化

  1. 基准值的选择,可以取两边界值、随机取值、多数中取中间值
  2. partition的选择
  3. 当区间的元素数量小到一个阈值时,可以考虑用插入排序

归并排序

原理

归并排序采用采用的是归并操作思想,将一个区间分为两个区间,在将每个子区间细分为两个区间,直到每个子区间在各自区间上都有序,再将子区间合并使合成后的区间有序,直到最终合成为一个区间,即将区间分割成若干的有序区间,在将有序区间两两合并

实现

public static void mergeSort(int[] arr){
        mergeSortFunction(arr,0,arr.length);
        //待排序区间[0,arr.length)
    }

    private static void mergeSortFunction(int[] arr, int low, int high) {
        if(low + 1 >= high){
            return;
        }
        int mid = (low + high) / 2;
        mergeSortFunction(arr,low,mid);
        mergeSortFunction(arr,mid,high);

        merge(arr,low,mid,high);
    }

    private static void merge(int[] arr, int low, int mid, int high) {
        int i = low;
        int j = mid;
        int len = high-low;
        int[] newArray = new int[len];
        int k = 0;
        while(i < mid && j < high){
            if(arr[i] < arr[j]){
                newArray[k++] = arr[i++];
            }else{
                newArray[k++] = arr[j++];
            }
        }
        while (i < mid){
            newArray[k++] = arr[i++];
        }
        while (j < high){
            newArray[k++] = arr[j++];
        }
        for(k = 0; k < len; k++){
            arr[low+k] = newArray[k];
        }
    }

性能

时间复杂度:O(n*log(n))
空间复杂度:O(n)

对海量数据进行排序

外部排序:排序过程需用到磁盘等外部储存空间竞选排序
前提:内存只有1G,需要排序100G
因为内存中因为无法一次放下所有数据,所以需要外部排序,其中归并排序是最常用的外部排序

  1. 将数据分为200份,每份512m
  2. 分别对每份数据放到内存中进行排序,在写回磁盘中
  3. 进行200路归并,对200分自身有序文件同时左归并排序,排序好的数据写会磁盘

排序算法的稳定性

概念:

何为排序稳定?即在对一个无序区间排序时,参照值相同的数据,排序前后相对位置没有改变,则称该排序算法稳定
在这里插入图片描述

具有稳定性的排序算法:

冒泡、插排、归并

稳定性存在的意义:

那么既然比较的两个数据值一样,那考虑谁先谁后有什么意义呢?
当对一堆具有复杂属性的数据进行排序时,由可能排序时只按其中的一个属性进行排序,此时如果排序不稳定,则会导致两个值相同的数据位置混乱,造成混淆。

七大排序算法的比较

在这里插入图片描述
以上便是对七大排序算法的知识点小结,随着后续学习的深入还会同步的对内容进行补充和修改,如能帮助到各位博友将不胜荣幸,敬请斧正

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值