七大排序算法

七大排序应用场景

数据量规模较小,考虑插入或选择。当元素分布有序时插入将大大减少比较和移动记录的次数,如果不要求稳定性,可以使用选择,效率略高于插入;

数据量规模中等,使用希尔排序;

数据量规模较大,考虑堆排序(元素分布接近正序或逆序)、快速排序(元素分布随机)和归并排序(稳定性);

一般来说不使用冒泡。

性能总结

交换排序 :1、冒泡排序;2、快速排序
选择排序 :3、选择排序;4、堆排序
插入排序 :5、直接插入排序;6、希尔排序
归并排序:7、归并排序

1、冒泡排序

双指针

时间复杂度:O(N^2)

空间复杂度:O(1)

稳定性:稳定

java 复制代码
    //冒泡排序:时间复杂度O(n^2);空间复杂度O(1);稳定
    public static void bubbleSort(int[] array){
        for(int i=0;i<array.length-1;i++){//趟数
            boolean sort = true;
            for(int j=0;j<array.length-i-1;j++){
                if(array[j] > array[j+1]){//交换,大值往后走
                    int tmp = array[j];
                    array[j] = array[j+1];
                    array[j+1] = tmp;
                    sort = false;//在交换证明无序
                }
            }
            if(sort == true)//如果已经有序的话,直接返回
                return;
        }
    }

2、快速排序

时间复杂度:最好O(NlgN),最坏O(N^2)

空间复杂度:O(lgN)

稳定性:不稳定

java 复制代码
    //快速排序:时间复杂度最好O(NlogN),最坏O(N^2);空间复杂度O(logN);不稳定
    public static void quickSort(int[] array,int low,int high){
        int start = low;
        int end = high;
        int key = array[start];//基准
        while(start < end){
            while(end>start && array[end]>=key){//从右边找小于基准key的值
                end--;
            }
            if(array[end] < key){//小于基准的值放基准key左边
                int tmp = array[end];
                array[end] = array[start];
                array[start] = tmp;
            }
            while(end>start && array[start]<key){//从左边找大于基准的值
                start++;
            }
            if(array[start] > key){//大于放右边
                int tmp = array[start];
                array[start] = array[end];
                array[end] = tmp;
            }
        }
        if(start > low){//证明start往右边走了,low和start之间还存在无序,继续排序
            quickSort(array,low,start-1);
        }
        if(high > end){//证明end往左走了,end和high之间无序,继续排序
            quickSort(array,end+1,high);
        }
    }

3、选择排序

时间复杂度:O(N^2)

空间复杂度:O(1)

稳定性:不稳定

java 复制代码
    //选择排序:时间复杂度O(n^2);空间复杂度:O(1);不稳定
    public static void selectSort(int[] array){
        int index = 0;
        for(int i=0;i<array.length;i++){//趟数
            int tmpMin = array[i];//每趟默认当前位置值在剩下的所有值中最小
            for(int j=i;j<array.length;j++){//在剩下位置中找到最小值
                if(array[j]<=tmpMin){
                    tmpMin = array[j];
                    index = j;
                }
            }
            array[index] = array[i];//交换最小值和i位置的值
            array[i] = tmpMin;
        }
    }

4、堆排序

时间复杂度:O(N*lgN)

空间复杂度:O(1)

稳定性:不稳定
堆排序步骤分三步

1、对初始数组构造大根堆;

2、将堆顶元素交换到最后;

3、对剩余数组构造大根堆

java 复制代码
    public static void heapSort(int[] arr) {
        //构造大根堆
        heapInsert(arr);
        int size = arr.length;
        while (size > 1) {
            //固定最大值
            swap(arr, 0, size - 1);
            size--;
            //构造大根堆
            heapify(arr, 0, size);

        }

    }

    //构造大根堆(通过新插入的数上升)
    public static void heapInsert(int[] arr) {
        for (int i = 0; i < arr.length; i++) {
            //当前插入的索引
            int currentIndex = i;
            //父结点索引
            int fatherIndex = (currentIndex - 1) / 2;
            //如果当前插入的值大于其父结点的值,则交换值,并且将索引指向父结点
            //然后继续和上面的父结点值比较,直到不大于父结点,则退出循环
            while (arr[currentIndex] > arr[fatherIndex]) {
                //交换当前结点与父结点的值
                swap(arr, currentIndex, fatherIndex);
                //将当前索引指向父索引
                currentIndex = fatherIndex;
                //重新计算当前索引的父索引
                fatherIndex = (currentIndex - 1) / 2;
            }
        }
    }
    //将剩余的数构造成大根堆(通过顶端的数下降)
    public static void heapify(int[] arr, int index, int size) {//index-父节点的索引,size-剩余数组大小
        int left = 2 * index + 1;
        int right = 2 * index + 2;
        while (left < size) {
            int largestIndex;
            //判断孩子中较大的值的索引(要确保右孩子在size范围之内)
            if (arr[left] < arr[right] && right < size) {
                largestIndex = right;
            } else {
                largestIndex = left;
            }
            //比较父结点的值与孩子中较大的值,并确定最大值的索引
            if (arr[index] > arr[largestIndex]) {
                largestIndex = index;
            }
            //如果父结点索引是最大值的索引,那已经是大根堆了,则退出循环
            if (index == largestIndex) {
                break;
            }
            //父结点不是最大值,与孩子中较大的值交换
            swap(arr, largestIndex, index);
            //将索引指向孩子中较大的值的索引
            index = largestIndex;
            //重新计算交换之后的孩子的索引
            left = 2 * index + 1;
            right = 2 * index + 2;
        }

    }
    //交换数组中两个元素的值
    public static void swap(int[] arr, int i, int j) {
        int temp = arr[i];
        arr[i] = arr[j];
        arr[j] = temp;
    }

5、直接插入排序

时间复杂度:最坏情况下O(N^2),最好情况下O(N),(只用i动,j不用走)

空间复杂度:O(1)

稳定性:稳定
直接插入排序步骤分三步

1、循环选择无序区间的第一个值作为要插入的值

2、将要插入的值往前比较,直到前一个数比插入的值小

3、插入

java 复制代码
    //直接插入排序
    public static int[] insertsort(int[] arr){
        for(int i=1;i<arr.length;i++){
            int insertVal = arr[i];//保存要插入的值
            int index = i-1;//准备往前比较
            while(index>=0&&arr[index]>=insertVal){//当插入的值比被插入的值小
                arr[index+1] = arr[index];//则将arr[index]向后移动
                index--;//index向前移动
            }
            //找到插入的值的位置,插入
            arr[index+1] = insertVal;
        }
        return arr;
    }

6、希尔排序(插入排序的一种)

时间复杂度:O(NlgN)~O(N^2)

空间复杂度:O(1)

稳定性:不稳定

希尔排序是对直接插入排序的优化,和gap的取值有关

java 复制代码
    //希尔排序
    public void shellSort(int[] array){
        int gap = 3;
        while(gap>0){
            for(int i=gap;i<array.length;i++){
                int insertVal = array[i];
                int index = i-gap;
                while(index>=0 && array[index]>insertVal){
                    array[index+gap] = array[index];
                    index -= gap;
                }
                array[index+gap] = insertVal;
            }
            gap--;
        }
    }

7、归并排序

时间复杂度:O(NlgN)

空间复杂度:O(N)

稳定性:稳定

java 复制代码
    //归并排序
    public  static int[] mergesort(int[] data){
        sort(data,0,data.length-1);
        return data;
    }

    public static void sort(int[] data,int left,int right){
        if(left >= right){
            return;
        }
        int center = (left+right)/2;
        sort(data,left,center);//递归左边数组
        sort(data,center+1,right);//递归右边数组
        merge(data,left,center,right);
    }
    public static void merge(int[] data,int left,int center,int right){
        int[] tmpArr = new int[data.length];//临时数组
        int mid = center+1;//右边数组的第一个索引
        int third = left;//临时数组的索引
        int tmp = left;//左边数组的第一个索引
        while(left <= center&&mid <= right){//比较左右数组元素的大小,小的放入临时数组
            if(data[left]<=data[mid]){
                tmpArr[third++] = data[left++];
            }else{
                tmpArr[third++] = data[mid++];
            }
        }
        //将剩下的一个数组的剩余部分放入临时数组
        while(mid<=right){
            tmpArr[third++] = data[mid++];
        }
        while(left<=center){
            tmpArr[third++] = data[left++];
        }
        //拷贝回原数组
        while(tmp <= right){
            data[tmp] = tmpArr[tmp++];
        }
    }
相关推荐
南宫生29 分钟前
力扣-数据结构-3【算法学习day.74】
java·数据结构·学习·算法·leetcode
向宇it1 小时前
【从零开始入门unity游戏开发之——C#篇30】C#常用泛型数据结构类——list<T>列表、`List<T>` 和数组 (`T[]`) 的选择
java·开发语言·数据结构·unity·c#·游戏引擎·list
A懿轩A1 小时前
C/C++ 数据结构与算法【树和二叉树】 树和二叉树,二叉树先中后序遍历详细解析【日常学习,考研必备】带图+详细代码
c语言·数据结构·c++·学习·二叉树·
DogDaoDao3 小时前
leetcode 面试经典 150 题:矩阵置零
数据结构·c++·leetcode·面试·矩阵·二维数组·矩阵置零
徐子童3 小时前
二分查找算法专题
数据结构·算法
小王子10244 小时前
数据结构与算法Python版 二叉查找树
数据结构·python·算法·二叉查找树
DoNow☼4 小时前
什么是数据结构
数据结构
凭君语未可5 小时前
详解归并排序
算法·排序算法
Dong雨8 小时前
六大排序算法:插入排序、希尔排序、选择排序、冒泡排序、堆排序、快速排序
数据结构·算法·排序算法
茶猫_8 小时前
力扣面试题 39 - 三步问题 C语言解法
c语言·数据结构·算法·leetcode·职场和发展