算法 - 排序 - 归并排序

11 篇文章 0 订阅

1. 基本思想

归并排序(MERGE-SORT)是利用归并的思想实现的排序方法,该算法采用经典的分治(divide-and-conquer)策略(分治法将问题(divide)成一些小的问题然后递归求解,而治(conquer)的阶段则将分的阶段得到的各答案"修补"在一起,即分而治之)。

分而治之

可以看到这种结构很像一棵完全二叉树,本文的归并排序我们采用递归去实现(也可采用迭代的方式去实现)。阶段可以理解为就是递归拆分子序列的过程,递归深度为log2n。

2. 合并相邻有序子序列

再来看看阶段,我们需要将两个已经有序的子序列合并成一个有序序列,比如上图中的最后一次合并,要将[4,5,7,8]和[1,2,3,6]两个已经有序的子序列,合并为最终序列[1,2,3,4,5,6,7,8],来看下实现步骤。

3. 代码实现

递归方式

package com.algorithm;

import java.util.Arrays;
import java.util.Random;

/**
 * Created by lizq on 2020/3/1.
 */
enum TYPE {
    MOVE, NEW
}

public class MergeSort {

    public static void main(String[] args) {

        long[] arrs = RandomArr.createLongArr(20, 0, 200);
        long[] arrs2 = Arrays.copyOf(arrs, arrs.length);
        Arrays.stream(arrs).forEach(i -> {
            System.out.print(i + " ");
        });
        System.out.println();
        System.out.println("mergeMove");
        sort(arrs, 0, arrs.length - 1, TYPE.MOVE);
        Arrays.stream(arrs).forEach(i -> {
            System.out.print(i + " ");
        });

        System.out.println();
        System.out.println("mergeNewArr");
        sort(arrs2, 0, arrs2.length - 1, TYPE.NEW);
        Arrays.stream(arrs2).forEach(i -> {
            System.out.print(i + " ");
        });
        System.out.println();

    }


    /**
     * 使用递归方式进行排序
     * @param arr
     * @param from
     * @param to
     * @param type
     */
    public static void sort(long[] arr, int from, int to, TYPE type) {
        if (to - from < 2) {
            if (arr[from] > arr[to]) {
                long tmp = arr[from];
                arr[from] = arr[to];
                arr[to] = tmp;
            }
        } else {
            int m = (from + to) / 2;
            sort(arr, from, m, type);
            sort(arr, m + 1, to, type);
            if (TYPE.MOVE == type) {
                mergeMove(arr, from, to, m + 1);
            } else if (type.NEW == type) {
                mergeNewArr(arr, from, to, m + 1);
            }

        }
    }


    /**
     * 不创建新的数组,但是会频繁移动数据,增加时空复杂度
     *
     * @param arr
     * @param from
     * @param to
     */
    public static void mergeMove(long[] arr, int from, int to, int r) {
        for (int i = from; i < to && r <= to; i++) {
            if (arr[i] > arr[r]) {
                long tmp = arr[r];
                for (int j = r; j > i; j--) {
                    arr[j] = arr[j - 1];
                }
                arr[i] = tmp;
                r++;
            }
        }
    }

    /**
     * 创建新的数组,进行数据暂存
     * @param arr
     * @param from
     * @param to
     * @param r
     */
    public static void mergeNewArr(long[] arr, int from, int to, int r) {
        long[] lArr = new long[r - from];
        System.arraycopy(arr, from, lArr, 0, r - from);
        long[] rArr = new long[to - r + 1];
        System.arraycopy(arr, r, rArr, 0, to - r + 1);
        for (int i = 0, j = 0, k = from; i < lArr.length && j < rArr.length; k++) {
            if (lArr[i] > rArr[j]) {
                arr[k] = rArr[j];
                j++;
            } else {
                arr[k] = lArr[i];
                i++;
            }
            if (i == lArr.length) {
                System.arraycopy(rArr, j, arr, ++k, rArr.length - j);
                break;
            }
            if (j == rArr.length) {
                System.arraycopy(lArr, i, arr, ++k, lArr.length - i);
                break;
            }
        }
    }
}

forkJoin方式

package ThreadTest.demo;

import lombok.Data;

import java.util.Arrays;
import java.util.Random;
import java.util.concurrent.*;

/**
 * Created by lizq on 2020/3/1.
 */
public class ForkJoinTest {

    public static void main(String[] args) throws ExecutionException, InterruptedException {

        long[] arrs = RandomArr.createLongArr(30, 0, 100);
        Arrays.stream(arrs).forEach(i -> {
            System.out.print(i + " ");
        });
        long rlt = Arrays.stream(arrs).sum();
        System.out.println("sum " + rlt);

        // ForkJoinPool forkJoinPool = ForkJoinPool.commonPool();
        ForkJoinPool forkJoinPool = new ForkJoinPool(5);
        // ForkJoinTask<Long> forkJoinTask = forkJoinPool.submit(new SumTask(arrs, 0, arrs.length - 1));
        //  rlt = forkJoinTask.get();
        // System.out.println("sum " + rlt);
        ForkJoinTask forkJoinTask = forkJoinPool.submit(new orderTask(arrs, 0, arrs.length - 1));
        forkJoinTask.get();
        Arrays.stream(arrs).forEach(i -> {
            System.out.print(i + " ");
        });

    }
}


class orderTask extends RecursiveAction {

    private long[] arr;
    private int from;
    private int to;


    public orderTask(long[] arr, int from, int to) {
        this.arr = arr;
        this.from = from;
        this.to = to;

    }

    @Override
    protected void compute() {

        //   System.out.print("thread begin : " + Thread.currentThread().getName() + " from= " + from + "; to=" + to);
        if (to - from < 2) {
            if (arr[from] > arr[to]) {
                long tmp = this.arr[from];
                this.arr[from] = this.arr[to];
                this.arr[to] = tmp;
            }
        } else {
            int m = (from + to) / 2;
            orderTask leftSum = new orderTask(this.arr, from, m);
            orderTask rightSum = new orderTask(this.arr, m + 1, to);
            leftSum.fork();
            rightSum.fork();
            leftSum.join();
            rightSum.join();
            // 组合排序
            for (int l = from, r = m + 1; l < to && r <= to; l++) {
                if (arr[l] > arr[r]) {
                    long tmp = arr[r];
                    for (int i = r; i > l; ) {
                        arr[i] = arr[--i];
                    }
                    arr[l] = tmp;
                    r++;
                }
            }
        }
        //  System.out.print("thread end : " + Thread.currentThread().getName() + " from= " + from + "; to=" + to);

    }
}

class RandomArr {
    public static long[] createLongArr(int length, int low, int high) {
        if (length < 1) {
            throw new RuntimeException("length < 1");
        }
        return new Random().longs(low, high).limit(length).toArray();
    }
}

4. 总结

归并排序是稳定排序,它也是一种十分高效的排序,能利用完全二叉树特性的排序一般性能都不会太差。java中Arrays.sort()采用了一种名为TimSort的排序算法,就是归并排序的优化版本。从上文的图中可看出,每次合并操作的平均时间复杂度为O(n),而完全二叉树的深度为|log2n|。总的平均时间复杂度为O(nlogn)。而且,归并排序的最好,最坏,平均时间复杂度均为O(nlogn)。

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
排序是计算机科学中常见的操作,它将一组元素按照特定的顺序重新排列。排序算法的目标通常是将元素按照升序或降序排列。 常见的排序算法有很多种,每种算法都有不同的时间复杂度和空间复杂度。以下是几种常见的排序算法: 1. 冒泡排序(Bubble Sort):比较相邻的两个元素,如果顺序不正确就交换位置,每次遍历将一个最大(最小)的元素移到最后(最前)。时间复杂度为O(n^2)。 2. 插入排序(Insertion Sort):将数组分为已排序和未排序两部分,每次从未排序部分取出一个元素,插入已排序部分的适当位置。时间复杂度为O(n^2)。 3. 选择排序(Selection Sort):每次从未排序部分选择一个最小(最大)的元素放到已排序部分的末尾。时间复杂度为O(n^2)。 4. 快速排序(Quick Sort):选取一个基准元素,将数组划分为两个子数组,小于基准元素的放在左边,大于基准元素的放在右边,然后对子数组进行递归排序。时间复杂度平均情况下为O(nlogn),最坏情况下为O(n^2)。 5. 归并排序(Merge Sort):将数组递归分成两个子数组,然后对子数组进行排序,最后将两个已排序的子数组合并成一个有序数组。时间复杂度为O(nlogn)。 6. 堆排序(Heap Sort):将数组构建成一个最大(最小)堆,每次从堆顶取出最大(最小)元素放到已排序部分的末尾,然后调整堆使其满足堆的性质。时间复杂度为O(nlogn)。 这里只介绍了几种常见的排序算法,每种算法都有其适用的场景和优缺点。在实际应用中,根据数据规模和性能要求选择合适的排序算法非常重要。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值