十大排序算法与代码

推荐学习链接:https://www.bilibili.com/video/BV1Wq4y1Z7yi?p=1

在这里插入图片描述

选择排序

/**
 * 选择排序 - (时间复杂度 n2) - (空间复杂度 1) - 不稳定 :
 * 记录当前 未排序部分 最小值的 下标 ,
 * 然后,与正确位置处的值进行交换。
 */
public class 选择排序 {

    private static void sort(int[] nums){
        int minIndex = 0;  //记录当前 未排序部分 最小值的 下标
        for (int i = 0; i < nums.length-1 ; i++) {
            minIndex = i;
            //更新 未排序部分 最小值的下标
            for (int j = i+1; j < nums.length; j++) {
                if (nums[minIndex] > nums[j])  minIndex = j;
            }
            int t = nums[i];
            nums[i] = nums[minIndex];
            nums[minIndex] = t;
        }
    } 
       
}

插入排序

/**
 * 插入排序 - (时间复杂度 n2) - (空间复杂度 1) - 稳定 :
 * 保证前面 部分数据有序,将后面数据依次与前面有序部分的数据进行比较,移动,
 * 然后,将此待排数据插入到前面合适的位置中。
 */
public class 插入排序 {

    private static void sort(int[] nums){
        int temp;
        for (int i = 1; i < nums.length; i++) {
            temp = nums[i]; //现在要插入i位置上的牌
            int j ; //i前面的部分是有序的
            for (j = i-1; j >=0 && temp < nums[j]; j--) {
                nums[j+1] =nums[j];
            }
            nums[j+1] = temp;
        }
    }
    
}

冒泡排序

/**
 * 冒泡排序 - (时间复杂度 n2) - (空间复杂度 1) - 稳定 :
 * 每次循环 ,在前面未排序部分,比较相邻数据的大小,
 * 通过相邻数据之间的比较以及交换,每次循环结束都可以把当前未排序部分的最大值移动到后面。
 * 后面是有序部分,而且有序部分会越来越多,最后整个数组就是有序数组了。
 */
public class 冒泡排序 {

    private static void sort(int[] nums){
        for(int i = nums.length;i > 0;i--){
            for(int j = 1;j < i;j++){
                if(nums[j-1] > nums[j]){
                    int t = nums[j];
                    nums[j] = nums[j-1];
                    nums[j-1] = t;
                }
            }
        }
    }
    
}

希尔排序

/**
 * 希尔排序 - (时间复杂度 n(1.3) ) - (空间复杂度 1) - 不稳定 :
 * 希尔排序 是对 插入排序 的一种改进和升级。
 * 希尔排序利用插入排序的思想,在插入排序的基础上:
 * 将整个序列以固定间隔,分成若干个子序列,首先在子序列内部分别进行插入排序,
 * 然后,再不断的缩小间隔,直到最后间隔为1时,对整个序列进行一次插入排序。
 */
public class 希尔排序 {

    private static void sort(int[] nums) {
        for (int d = nums.length/3; d >0 ; d = d/3) {   //d表示间隔,最后d=1时,表示对整个序列进行最终排列
            for (int i = d; i < nums.length; i++) {     //处理 下标为i处的数据,
                for (int j = i-d; j >=0  ; j=j-d) {     //进行插入排序:将下标为i的数据,插入到前面以d为间隔的有序子序列当中
                    if (nums[j+d] < nums[j]){
                        int t = nums[j+d];
                        nums[j+d] = nums[j];
                        nums[j] = t;
                    }
                }
            }
        }
    }

}

归并排序

/**
 * 归并排序 - (时间复杂度 n *(log n) ) - (空间复杂度 n) - 稳定 :
 * 归并排序的思想是:
 * 首先,把整个数组一分为二,分成两个子序列,
 * 然后,再对每个子序列进行分割,
 * 直到每个子序列中都只有一个元素时(单个元素的序列可以认为是有序的子序列),
 * 然后再依次合并两个有序子序列 ,成为一个更大的有序子序列,
 * 直到最终合并成一整个有序序列。
 *
 * Java里,进行 对象排序的 Arrays.sort (T[] a , Comparator<? super T> c) 方法,
 * JDK 1.8 以前,用的是上面说的,普通的归并排序,
 * JDK 1.8 开始,使用 TimSort.sort()方法,此方法先在局部采用二分插入排序,然后再使用多路归并排序。
 * TimSort.sort()方法中,规定 子数组的最小长度为 MIN_MERGE。
 * 多路归并排序的算法思想是,直接将原数组按照 MIN_MERGE长度,分割成多个子数组(不是用递归一点一点的分割),
 * 长度小于 MIN_MERGE的子数组,调用binarySort方法进行二分插入排序 (向有序部分插入待排数据时,使用二分法寻找插入位置)。
 * 所有 MIN_MERGE长度的子数组内部有序了之后,再两两合并、排序,最终实现整个数组有序。
 */
public class 归并排序 {

    private static void sort(int[] nums){
        div(nums,0,nums.length-1);
    }


    /**
     * div() 是个 递归函数:
     * 作用是,将当前数组一分为二,形成两个子数组,
     * 然后再对子数组进行分割,直到每个子数组都只有一个元素为止。
     * start 是开始下标 (包含),
     * end   是结束下标 (包含)。
     */
    private static void div(int[] nums, int start, int end){
        if (end == start) return;   //表示当前序列只有一个元素,递归结束

        int mid = (end - start)/2 + start;
        div(nums,start,mid);       //继续分割前半部分
        div(nums,mid+1,end);  //继续分割后半部分

        merge(nums,start,mid,end);   //合并两个有序子序列
    }



    /**
     * merge方法的作用是,合并两个有序的子数组:
     * 第一个子数组:以 start 开始(包含),以 mid结束(包含)。
     * 第二个子数组:以 mid+1 开始(包含),以 end结束(包含)。
     */
    private static void merge(int[] nums, int start, int mid, int end) {
        int i = start,j = mid+1;              //分别用 i和j, 记录两个有序数组的开始下标
        int[] temp = new int[end-start+1];    //临时保存合并之后的数据
        int p = 0;   //记录临时数组temp的下标位置

        while (i <= mid && j <= end){
            temp[p++] = (nums[i] <= nums[j]) ? nums[i++] : nums[j++];
        }
        //处理 某个子数组的剩余部分
        while (i <= mid) temp[p++] = nums[i++];
        while (j <= end) temp[p++] = nums[j++];

        //将合并结果传给原数组
        for (int k = 0; k < temp.length; k++) {
            nums[start+k] = temp[k];
        }
    }

}

快速排序

/**
 * 快速排序 - (时间复杂度 n *(log n)) - (空间复杂度 log n) - 不稳定 :
 * 算法思想:
 * 每次选取一个轴线值,然后分别从前和后两个方向,分别向中间移动前后指针,
 * 如果前面的指针在移动的过程中,遇到了比轴线值大的数,就立即停止,
 * 如果后面的指针在移动的过程中,遇到了比轴线值小的数,也立刻停止,
 * 此时,两个指针所在下标处的值进行交换,交换完之后,前后指针继续向中间移动,
 * 一直持续上述过程,直到两个指针相遇为止,表示一次快速排序完成。
 *
 * 注意:每完成一次快速排序后的结果:
 * 1.轴线值一定会在正确的位置上,
 * 2.此外,轴线前面的数一定小于等于轴线值,轴线后面的数一定大于等于轴线值。
 *
 * 然后,根据上一轮排序完成后的轴线下标,
 * 将数组分割成前后两个子数组,再分别对子数组进行快速排序。
 * 直到每个子数组都只有一个元素为止,表示整个数组已经完全有序。
 */
public class 快速排序 {

    private static void sort(int[] nums){
        div(nums,0,nums.length-1);
    }


    /**
     * div() 是个 递归函数,其作用是:
     * 在以 start 开始(包含),以 end 结尾(包含) 的数组中,
     * 先调用 partition()函数, 对当前数组进行一次快速排序,并获得排序完成后,轴线数据所在的正确下标。
     * 然后,再将数组按照轴线所在下标,分割成前后两个子数组,再调用partition()函数进行快速排序。
     * 直到最后每个子数组都只有一个元素为止。
     */
    private static void div(int[] nums, int start, int end) {
        if (start >= end) return;   //表示前后指针已经相遇,递归结束

        int mid = partition(nums,start,end);
        div(nums,start,mid-1); //继续处理前半部分
        div(nums,mid+1,end);   //继续处理后半部分
    }



    /**
     * partition()方法 的作用是:
     * 在以 start 开始(包含),以 end 结尾(包含) 的数组中,
     * 始终以 下标为start的数据(第一个数据) 作为轴线,进行一次快速排序,
     * 并返回排序完成后,轴线数据所在的正确位置下标。
     * 快速排序的思想是:
     * 首先有一个指针left,left从第二个元素开始向后搜索,当找到比轴线大的数据时就停下来,
     * 然后,让从后往前找的指针right向前移动,当找到比轴线小的数据时,也停下来,
     * 此时,交换left和right所在下标中的值。
     * left指针始终向后移动,right指针始终向前移动,直到left=right为止。
     * 最后,再将 轴线值 与 两指针交界处的值 进行判断,
     * 把 轴线值换到 正确的位置上去,
     * 并将轴线值所在的正确下标位置,作为返回值返回。
     */
    private static int partition(int[] nums, int start, int end) {
        int pivot = nums[start];    //选取子数组的第一个数作为轴值
        int left = start+1 , right = end;

        while (left < right){
            while (left < right && nums[left]  <= pivot) left++;
            while (left < right && nums[right] >= pivot) right--;

            //此时,下标为left的数 大于轴值pivot,下标为right的数 小于轴值pivot。二者进行值交换
            if (left < right){
                int t = nums[left];
                nums[left] = nums[right];
                nums[right] = t;
            }
        }
        //此时 left = right,最后再把 轴值pivot 换到 正确的位置上。
        if (pivot < nums[left])  left--;

        int t = nums[start];
        nums[start] = nums[left];
        nums[left] = t;

        return left; //返回轴线的所在下标
    }

}

计数排序

/**
 * 计数排序 是一种 非比较排序:
 * (时间复杂度 n+k) - (空间复杂度 n+k) 。
 * 思想:
 * 将待排序数据本身作为计数数组的下标,
 * 计数数组的下标就是数据本身,该下标内的值就是该数据的总个数。
 * 因此,计数排序适合于数据量大且数据范围有限的情况。
 */
public class 计数排序 {

    private static void sort(int[] nums){
        int[] count = new int[10];

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

        for (int i = 0,j = 0; i < count.length ; i++) {
            while (count[i]-- >0) nums[j++] = i;
        }
    }


    public static void main(String[] args) {
        int[] nums = {3,6,1,8,2,5,7,3,8,0,4,9,6,1};
        sort(nums);
        for(int i = 0; i < nums.length; i++) {
            System.out.print(nums[i]+"\t");
        }
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值