【算法】七大排序详解(上篇)

本文详细介绍了四种基础排序算法:选择排序、冒泡排序、直接插入排序及其优化版本,以及堆排序。每种排序算法的原理、时间复杂度和稳定性进行了分析,并提供了相应的Java实现代码。对于插入排序,还特别提到了在近乎有序的数据集上的优秀性能。此外,文章预告了后续会讲解的希尔排序、快速排序和归并排序。
摘要由CSDN通过智能技术生成

1.选择排序

排序思路:将待排序数组分为有序区间和无序区间,每次从无序区间选择一个元素添加到有序区间指定位置,直至整个数组有序;
时间复杂度:O(n^2)
稳定性:不稳定

代码:

	/**
     * 选择排序
     * @param arr
     */
    public static void selectionSort(int[] arr){
        //优化
        if (arr==null || arr.length<2){
            //只有一个元素或者没有元素 直接返回
            return;
        }
        for (int i=0;i<arr.length-1;i++){
            //min对应无序最小值的索引
            int min=i;
            for (int j=i+1;j<arr.length;j++){
                //更新最小元素的索引
                min =arr[j]<arr[min] ?j:min;
            }
            //经过内层循环,min对应无序区间的最小值,将最小值交换到有序区间
            swap(arr,i,min);
        }
    }
    
    //交换元素
    private static void swap(int[] arr, int i, int j) {
    	int temp= arr[i];
    	arr[i] =arr[j];
    	arr[j]=temp;
    }

优化版本:双向选择排序

/**
     * 双相选择排序
     * 性能优于单向选择排序
     * @param arr
     */
    public static void selectionSortOP(int[] arr){
        //定义两个变量分别从数组的头尾双向选择
        int low=0;
        int high=arr.length-1;
       while (low<high){
            int min=low;            //最小值对应的索引
            int max=low;            //最大值对应的索引
           for (int i = low+1; i <=high ; i++) {
                if (arr[i]<arr[min]){
                    min=i;
                }
                if (arr[i]>arr[max]){
                    max =i;
                }
           }
       	   //经过以上循环,min对应区间的最小值,max对应区间的最大值
           //交换min和low位置
           swap(arr,min,low);
           if (max ==low){
               //最大值已经被交换到min这个位置
               max =min;
           }
           swap(arr,max,high);
           low+=1;
           high-=1;
        }
    }
    //交换元素
    private static void swap(int[] arr, int i, int j) {
    	int temp= arr[i];
    	arr[i] =arr[j];
    	arr[j]=temp;
    }

2.冒泡排序

排序思路:遍历原数组,依次比较前后元素的大小,如果后一元素比前一元素小,则交换它们位置,否则继续向后遍历,直至整个数组有序;
时间复杂度:O(n^2)
稳定性:稳定

代码:

/**
     * 冒泡排序的优化算法
     * @param arr
     */
    public static void bubbleSortPlus(int[] arr){
        int temp;
        for (int i = 0; i < arr.length ; i++) {
            //布尔值标记当前数组是否已经达到有序状态
            boolean flag =true;
            for (int j = 0; j < arr.length-1-i ; j++) {
                if (arr[j]>arr[j+1]){
                    temp=arr[j];
                    arr[j]=arr[j+1];
                    arr[j+1]=temp;
                    //本次发生了交换
                    flag =false;
                }
            }
            //根据标记值判断数组是否有序,如果有序则退出,无序继续循环
            if (flag){
                break;
            }
        }
    }

3.直接插入排序

排序思路:每次从无序区间选择一个数插入到合适的位置,插入过程类似于扑克牌的码牌过程,插入排序虽然是O(N^2)的排序算法,但是在近乎有序的数据集上,插入排序的性能甚至优于NlogN的排序算法,所以,插入排序也作为其他高阶排序算法的优化手段;
时间复杂度:O(N^2)
稳定性:稳定

代码:

/**
     * 冒泡排序
     * @param arr
     */
    public static void bubbleSort(int[] arr){
        if (arr ==null || arr.length<2){
            return;
        }
        for (int i = arr.length-1; i >0 ; i--) {
            for (int j = 0; j < i ; j++) {
                if (arr[j]>arr[j+1]){
                    swap(arr,j,j+1);
                }
            }
        }
    }

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

优化:引入标记位

  /**
     * 直接插入排序
     * 时间复杂度:O(N^2)
     * 稳定
     * 在近乎有序的数集上,直接插入排序性能非常好,甚至由于NlogN的排序算法
     * 插入排序常常作为高阶排序算法的优化算法
     * @param arr
     */
public static void insertionSort(int[] arr){
        for (int i = 1; i < arr.length ; i++) {
            //从待排序区间不断向前看,找到合适的插入位置
            for (int j = i; j >0 ; j--) {
                if (arr[j]>=arr[j-1]){
                    //表明arr[j]已经有序,相等也不交换,保证稳定性
                    break;
                }else {
                    swap(arr,j,j-1);
                }
            }
            //以上代码等同于
           /* for (int j=i;j>0 &&arr[j]<arr[j-1];j--){
                swap(arr,j,j-1);
            }*/
        }
    }
        //交换元素
    private static void swap(int[] arr, int i, int j) {
        int temp= arr[i];
        arr[i] =arr[j];
        arr[j]=temp;
    }

优化:折半插入排序
思路:

1.待排序原数组
在这里插入图片描述
2.将待排序数组分为有序区间【0,i)和无序区间【i,n】,从无序区间第一个元素开始,从有序区间选择合适的插入位置
在这里插入图片描述
3.找到有序区间的中间位置,将无需区间待排序的元素插入到合适的位置
在这里插入图片描述
4.找到待插入的位置后,将【L,i】之间的元素向后搬移一个位置,将当前遍历的元素插入到L位置
在这里插入图片描述

代码:

/**
     * 二分插入排序---插入排序的优化
     * @param arr
     */
    public static void insertSortBS(int[] arr){
        //有序区间:[0,i)        无序区间:[i,n]
        for(int i=1;i< arr.length;i++){
            int val =arr[i];
            int left =0;
            int right =i;
            while (left<right){
                int mid =left+((right-left)>>1);              //相当于(left+right)/2
                if (val < arr[mid]) {
                    right=mid;
                }else {
                    left=mid+1;
                }
            }
            //搬移[left,i]的元素
            for (int j=i;j>left;j--){
                arr[j]=arr[j-1];
            }
            arr[left] =val;
        }
    }

4.堆排序

排序思路:任意数组都可视为一颗完全二叉树,将数组调整为一个最大堆,然后再将元素下沉,重复以上操作,数字就调整为有序;
时间复杂度:O(nlogN)
稳定性:稳定

代码:

    /**
     * 堆排序
     * 时间复杂度: nlogN
     * 稳定排序算法
     */
    public static void heapSort(int[] arr){
        //1、先将数组调整为最大堆
        //从最后一个非叶子节点来时进行元素下沉
        for (int i=(arr.length-1-1)/2; i>=0;i--){
            //下沉终止位置就是arr.length
            siftDown(arr,i,arr.length);
        }
        //此时,数组已经被调整为最大堆,遍历数组
        for (int i = arr.length-1; i >0 ; i--) {
            //arr[0]:堆顶元素,就是最大值
            swap(arr,0,i);
            siftDown(arr,0,i);
        }
    }

    /**
     * 元素下沉操作
     * @param arr
     * @param i     当前下沉的索引
     * @param length 数组长度
     */
    private static void siftDown(int[] arr, int i, int length) {
        //只有当前节点存在左右孩子时元素才可以下沉
        while (2*i+1<length){
            int j =(i<<1)+1;
            if (j+1<length && arr[j+1]> arr[j]){
                //此时,右子树为最大值
                j=j+1;
            }
            //当前元素已经大于左右子树的最大值
            if (arr[i] > arr[j]){
                //下沉结束
                break;
            }else {
                //交换俩元素的位置
                swap(arr,i,j);
                //继续向后遍历,下沉元素
                i=j;
            }
        }
    }
     //交换元素
    private static void swap(int[] arr, int i, int j) {
        int temp= arr[i];
        arr[i] =arr[j];
        arr[j]=temp;
    }

希尔排序、快速排序、归并排序,请看下篇博客:【算法】七大排序详解(下篇)

创作不易,麻烦亲点赞收藏啦

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值