java实现十大经典排序算法(二)

java实现十大经典排序算法(二)

归并排序

算法思想:归并排序采用分治的思想,先将数组进行拆分成一个个子序列,直到拆到只剩一个元素,然后再进行有序合并,最后合并成一个有序的数组。

时间复杂度:使用分治递归的思想,T(n) = O(nlogn)

    // 归并排序
    public static void MergeSort(SortArray arr,int low, int high){
        if(low>=high)   return;
        int mid = (low+high)/2;
		
		// 递归归并
        MergeSort(arr,low,mid);
        MergeSort(arr,mid+1,high);

        int i = 0, j = low, k =mid+1;
        int [] temp = new int[100];

		//有序合并
        while(j<=mid && k<=high){
            if(arr.getNum(j)<arr.getNum(k)) temp[i++] = arr.getNum(j++);
            else temp[i++] = arr.getNum(k++);
        }
		
		// 多余的元素直接添加到数组末尾
        while(j<=mid) temp[i++] = arr.getNum(j++);
        while(k<=high) temp[i++] = arr.getNum(k++);

		// 将有序的临时数组返回到原数组
        for (i = low,j = 0; i <= high ; i++,j++) {
            arr.changeValue(i,temp[j]);
        }
    }

计数排序

算法思想:前面用到的排序算法都是基于比较思想,计数排序很巧妙,并不进行比较,更像提取出数组的特征,然后根据得到的特征数组获得排序后的数组。在一些特殊的应用场景(数据集中)算法效率很高。随着时间复杂度的降低,空间复杂度显著提高,但是,使用空间复杂度换取时间复杂度是值得的,而且算法的具体实现中还有很多小技巧以减少多余空间的使用。(从算法思想中可看出,计数排序并不适用于浮点数的排序
时间复杂度:时间复杂度为,T(n) = O(n+k),k为特征数组的长度,这里就是(max-min+1)

// 计数排序
    public static void CountSort(SortArray arr){
        int min = 101,max = -1;

        // 找到最大值和最小值
        for (int i = 0; i < arr.getLen(); i++) {
            if(arr.getNum(i) < min) min = arr.getNum(i);
            else if(arr.getNum(i)>max) max = arr.getNum(i);
        }

        int[] temp = new int[max-min+1];     // 减小多余空间使用,后面得数以min为基准

        for (int i = 0; i < arr.getLen(); i++) {
            temp[arr.getNum(i)-min] ++;
        }

        int j = 0;
        for (int i = 0; i < temp.length; i++) {
            while(temp[i]>0){
                arr.changeValue(j++,i+min);
                temp[i]--;
            }
        }

    }

桶排序

算法思想:与计数排序类似,但是又不同的地方,该算法是将数据先分为一个一个组(桶),在组(桶)内再进行排序(使用其他排序算法),然后依次将几个组(桶)内数据取出即可完成排序,桶排序适用于数据分布均匀的情况,这样各组数据分布也均匀,算法效率最高。(桶排序可以应用于浮点数排序
时间复杂度:在最理想的情况(数据均匀分布)下,T(n) 接近 O(n)
代码实现并不复杂,主要就是将数据分组,在组内使用其他排序算法(一般使用插入排序)进行排序,自己编写试试吧!

基数排序

算法思想:想一下我们平时是怎么比较数的大小的?先从最高位开始比较,最高位最大的一定最大,然后依次往下比较。计数排序就是这种思想,并且,基数排序有两种,分别是从高到低比较(MSD)和从低到高(LSD)比较。(这里介绍LSD)
时间复杂度:两重循环,T(n) = O( k*n ) ,k为数据的最大数据宽度(位数)。

public static void RadixSort(SortArray arr,int dataWidth){
        int n = 1;   // 数据宽度记录位,记录当前的排序是第几位
        int length = arr.getLen();
        int[][] bucket = new int[10][length];    // 二维数组,存储每次数据的分组结果
        int[] order = new int[10];        // 一维数组,记录0-9每组的数据元素多少

        while(n < dataWidth){
            // 将所有元素按照当前循环的位数,放到对应的桶中
            for (int i = 0; i < arr.getLen(); i++) {
                int temp = (arr.getNum(i)/n)%10;  // 取出对应位数
                bucket[temp][order[temp]++] = arr.getNum(i);
            }
            // 将桶中数据覆盖保存到原数组
            int k = 0;
            for(int i=0; i<10 ;i++) {
                for (int j = 0; j < order[i]; j++) {  // 一定要从小到大遍历,这样才不会破坏以前的排序结果
                    arr.changeValue(k++, bucket[i][j]);
                }
                order[i] = 0;  // order置零
            }
            n *= 10;  // 位数*10,用于下一次排序

        }

    }

堆排序

算法思想:堆排序使用到了堆这种数据结构(建议先去看看),而堆基于完全二叉树。算法首先构建一个大顶堆,然后循环,由于堆顶元素最大,每次将堆顶元素取出,将堆尾元素放到堆顶,重新构建大顶堆…直到堆的元素只剩一个,排序完成。
算法时间复杂度:堆排序的时间复杂度主要就是建堆所用。所以T(n) = O(nlogn)

    public static void HeapSort(SortArray arr) {
        for (int i = (arr.getLen() - 1) / 2; i >= 0; i--) {
            //从第一个非叶子结点从下至上,从右至左调整结构
            generateHeap(arr, i, arr.getLen());
        }

        //调整堆结构+交换堆顶元素与末尾元素
        for (int i = arr.getLen() - 1; i > 0; i--) {
            //将堆顶元素与末尾元素进行交换
            arr.swap(0,i);

            //重新对堆进行调整
            generateHeap(arr, 0, i);
        }

    }
    // 生成堆
    public static void generateHeap(SortArray arr,int parent,int length){
        //将temp作为父节点
        int temp = arr.getNum(parent);
        //左孩子
        int lChild = 2 * parent + 1;

        while (lChild < length) {
            //右孩子
            int rChild = lChild + 1;
            // 如果有右孩子结点,并且右孩子结点的值大于左孩子结点,则选取右孩子结点
            if (rChild < length && arr.getNum(lChild) < arr.getNum(rChild)) {
                lChild++;
            }

            // 如果父结点的值已经大于孩子结点的值,则直接结束
            if (temp >= arr.getNum(lChild)) {
                break;
            }

            // 把孩子结点的值赋给父结点
            arr.changeValue(parent,arr.getNum(lChild));

            //选取孩子结点的左孩子结点,继续向下筛选
            parent = lChild;
            lChild = 2 * lChild + 1;
        }
        arr.changeValue(parent,temp);
    }

完结

这一篇介绍的五种算法相较于上一篇要难一些,使用条件也较为苛刻,大家好好理解一下。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值