Java实现常用排序算法

常用的排序算法包括:
(1)插入排序√
(2)折半排序√
(3)希尔排序√
(4)选择排序√
(5)堆排序
(6)快速排序√
(7)归并排序√
(8)基数排序
(9)冒泡排序√

一、插入排序

基本思路:从前往后,每个数组插入到应该在的位置。如:第2个数字与第一个数字比较,根据二者大小判断是否应该转换位置;第3个数字在排序时由于前两个数字已经排好顺序,只要根据与前两个数字的大小关系判断,找到适合的位置插入进去,并将其他数字依次后移即可。以此类推。

代码:

static void sort(int[] nums){
        int len = nums.length;
        for(int i = 1; i < len; i++){
            int tmp = nums[i];//保存被排元素的数值
            for(int j = i-1; j >= 0; j--){
            //依次遍历被排元素之前的元素,找到一个位置,该位置前面的数值小于它,后面大于它
                if(nums[j] > tmp){
                    nums[j+1] = nums[j];
                }else{
                    nums[j+1] = tmp;
                    break;
                }
            }
        }

时间复杂度:O(n^2),对于每一个数字,都必须向前依次遍历寻找合适的位置
空间复杂度:O(1)
排序算法是稳定的(两个相同大小的数字,在排序后的相对位置不会改变,则称为稳定的排序算法)

二、折半排序

基本思路:在插入排序的基础上,当插入排序中比较操作的代价比交换操作大时,可采用二分查找的方法。由于在每一轮中,被排数字之前的数字都是已经排序好的,因此我们可以通过二分查找的方法来找到正确的插入位置。
代码:

static void sortB(int[] nums){
         int len = nums.length;
         for(int i = 1; i < len; i++){
             //用二分查找法找到应当插入的位置
             int tmp = nums[i];
             int left = 0, right = i-1;
             while(left <= right){//注意是<=
                 int mid = left + (right-left)/2;
                 if(nums[mid] < tmp){
                     left = mid+1;
                 }
                 if(nums[mid] > tmp){
                     right = mid-1;
                 }
             }//停止在left>right,right<tmp,left>tmp
             for(int j = i-1; j>=right ; j--){
                 nums[j+1] = nums[j];
             }
             nums[right+1] = tmp;
         }
    }

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

三、希尔排序

基本思路:由于插入排序对几乎已经有序的数据进行操作时,可以达到几乎线性的效率,故希尔排序先将整个待排序序列分为若干个子序列进行插入排序,再对整个序列进行直接排序。首先会给定一个步长gap,每隔gap的数字分为一组进行插入排序;排序完成后gao减小一半,继续进行插入排序,直到gap=1.

代码:

static void shellSort(int[] nums) {
        //先分组,距离为gap的为一组,然后gap不断缩小直到插入排序
        int len = nums.length;
        int gap = len/2;//一般初始距离为len/2
        while(gap > 0){
            for(int i = gap; i <len ; i++){
                //事实上,gap后的每一个数字都拥有依一次被排序的机会,因此没必要按照组进行两次for循环来确保按组排序
                 int tmp = nums[i];
                 for(int j = i-gap; j>=0; j -= gap){
                     if(nums[j] > tmp){
                         nums[j+gap] = nums[j];
                     }else{
                         nums[j+gap] = tmp;//放到空的位置
                         break;
                     }
                 }

             }
            gap = gap/2;
        }

    }

其时间复杂度介于O(nlogn) 到 O(n^2) 之间
空间复杂度为O(1)
希尔排序不是稳定的,因为希尔排序在插入元素的时候是跳跃性插入,有可能破坏稳定性。

四、选择排序

基本思路:每一轮选择其中最大的数字放在数组的尾端,进行n轮后全部排好
代码:

static void chooseSort(int[] nums){
        int len = nums.length;
        for(int i = 0; i< len ; i++){
            int min = i;
            for(int j = i+1; j<len; j++){
                if(nums[min] > nums[j]){
                    min = j;
                }
            }//找出最小值
            if(min != i){
            //放在最前端
                int tmp = nums[min];
                nums[min] = nums[i];
                nums[i] = tmp;
            }
        }
    }

最好情况时间复杂度、最坏情况和平均情况时间复杂度都为O(n^2)。
空间复杂度为O(1)
不稳定

五、堆排序

六、快速排序

基本思想:每一轮都选择一个元素(一般是选择左侧元素)作为基准,遍历整个数组,将比基准小的数字放在左侧,比基准大的数字放在右侧。一轮结束后,分别对左侧和右侧的数组递归采用快速排序。
代码:

static void quickSort(int[] nums,int left,int right){
        //快速排序
        if(left >= right){
            return ;
        }
        int base = nums[left];//每次都选择最左侧的数作为基准
        int i = left+1, j = right;
        while(i != j){
            while(nums[i] <= base && i<j){
                i++;//找到第一个比base大的数
            }

            while(nums[j] >= base && i<j){
                j--;//找到第一个比base小的数
            }

            //交换i和j
            if(i < j){
                int tmp = nums[i];
                nums[i] = nums[j];
                nums[j] = tmp;
            }//此时i的值是比base小的数,j是比base大的数,因此将i与base交换,仍不改变前面都是小于base的数
        }
        if(base>nums[i]){//只有当nums[i]<base时才移动
            nums[left] = nums[i];
            nums[i] = base;
        }
        quickSort(nums,left,i-1);
        quickSort(nums,i,right);
    }

最优的情况下空间复杂度为:O(logN)

最差的情况下空间复杂度为:O(N^2)

快速排序的平均时间复杂度是:O(N*logN)

因为在快速排序的时候,即使待排序元素可基数相等也需要移动待排序元素的位置使得有序,所以快速排序是不稳定的。

七、归并排序

基本思路:对于序列分为两个组,对两个组分别进行排序后再合并到一起排序,依次递归。合并的方法为:两个指针分别指向两组数据的第一个值,比较两个的大小,每次都取出最小的一个作为新数组的头部。
代码:

static void  merge(int[]nums,int low,int mid, int high){
        int[] nums1 = Arrays.copyOfRange(nums,low,mid+1);//最后一位不包含
        int[] nums2 = Arrays.copyOfRange(nums,mid+1,high+1);
        int i = 0, j = 0;
        for(int m = low; m <= high; m++){
            if(i < nums1.length && j < nums2.length){
                if(nums1[i] <= nums2[j]){
                    nums[m] = nums1[i++];
                }else{
                    nums[m] = nums2[j++];
                }
            }else if(i < nums1.length && j >= nums2.length){
                nums[m] = nums1[i++];
            }else if(i >= nums1.length &&  j <nums2.length){
                nums[m] = nums2[j++];
            }else{
                break;
            }
        }
    }
    static void mergeSort(int[] nums,int low,int high){
        System.out.println("low:"+low);
        System.out.println("high:"+high);
        int len = high-low+1;
        System.out.println("len:"+len);
        int mid = (high+low)/2;
        System.out.println("mid:"+mid);
        if(low >= high)
            return ;
        mergeSort(nums,low,mid);
        mergeSort(nums,mid+1,high);
        merge(nums,low,mid,high);
        for(int i = 0; i<len; i++){
            System.out.print(nums[i]+" ");
        }
    }

每次合并操作的时间复杂度是O(N),而二叉树的深度是log2N,所以总的时间复杂度是O(N*lgN)。
算法是稳定的

八、冒泡排序

基本思想:进行n轮比较,每一轮中比较相邻的两个数字,直到把最大的数字移到末端
代码:

static void bubbleSort(int[] nums){
        int len= nums.length;
        for(int i = 0; i < len; i++){
            for(int j = 1; j < len-i-1; j++){
                if(nums[j] < nums[j-1]){
                    int tmp = nums[j];
                    nums[j] = nums[j+1];
                    nums[j+1] = nums[j];
                }
            }
        }
    }

时间复杂度是O(N^2)
算法是稳定的

在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值