几种比较创新的排序算法,多线程排序

36 篇文章 1 订阅
28 篇文章 1 订阅

多线程

为什么要用多线程

很显然,多线程能够同时执行多个任务。举个例子,你打开某视频播放器,点击下载某个视频,然后你发现这个时候一直在下载,其他啥都干不了。所以在这种情况下,可以使用多线程,让下载任务继续同时也能继续其他操作

作为一个包工头,一堆砖要搬,但是就一个人,可是你只能搬这么多,怎么办?多找几个人一起搬,但是其他人就也需要付工钱,没关系,能早点干完也就行了,反正总体工钱差不多。

同样的,如果有一个任务特别耗时,而这个任务可以拆分为多个任务,那么就可以让每个线程去执行一个任务,这样任务就可以更快地完成了。

代价

听起来都很好,但是多线程是有代价的。由于它们“同时”进行任务,那么它们任务的有序性就很难保障,而且一旦任务相关,它们之间可能还会竞争某些公共资源,造成死锁等问题

总结

优点:

更快,加快处理任务
更强,同时处理多任务

缺点:

难控制,编程困难
不当使用降低性能,线程切换
bug难定位,资源竞争

排序

多线程排序

思路

假设有N个线程,则将数组数M据分为N组

每个线程对其中的一组数据使用库函数提供的快速排序算法

所有线程排序完成后,将每组排序好的数组合并

第一步
使用4个线程11个数据进行排序:

12,10,4,7,9,6,8,1,5,16,11

由于4不能被10整除,因此,前面三个线程,每个负责排序10%(4-1)= 3三个数,最后一个线程负责排序最后两个数
在这里插入图片描述
假设这4个线程都完成了自己的工作后,内容如下:
在这里插入图片描述
第二步

比较每组的第一个,选出最小的一个,这里是线程2的1放到新数组下标0处最后由主线程已经排好的每组进行合并

1放到新数组最开始的位置,线程的下次比较的内容后移,即下次比较时,比较线程2的第二个数。循环比较

最终可以得到合并的数据:

1 4 5 6 7 8 9 10 11 12 16

睡眠排序

睡眠排序也称为硬件排序,利用硬件计时器实现
时间复杂度为O(n)

原理构造n个线程,它们和这n个数一一对应初始化后线程们开始睡眠等到对应的那么多个时间单位后各自醒来,然后输出对应的数。这样最小的数对应的线程最早醒来,这个数最早被输出。等所有线程都醒来,排序就结束了。

当出现一个很大的数字,会睡眠很长时间(效率低)

当数字相差很小,会不精准(不精准)

不能处理负数(虽然可以在加上一个正数,再进行睡眠排序,但是得不偿失)

排序使用到了多线程,有点“杀鸡焉用宰牛刀”的感觉
多线程
public class SortTest {
    public static void main(String[] args) {
        int[] an = {123, 22, 9130000, 7, 333, 450, 3};
        for (int i = 0; i < an.length; i++) {
            new SleepThread(an[i]).start();
        }
    }
}

class SleepThread extends Thread {

    private int number;

    SleepThread(int number) {
        this.number = number;
    }

    @Override
    public void run() {
        try {
            Thread.sleep(number);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(number + "");
    }
}
多线程同步
public class SortTest {
    public static void main(String[] args) {
        int[] arr = {3, 5, 2, 4, 1, 4, 7, 3, 3000, 0};
        System.out.println("原数组" + Arrays.toString(arr));
        int[] res = sleepSort(arr);
        System.out.println("排序后" + Arrays.toString(res));
    }

    public static int[] sleepSort(int[] array) {
        int[] arr = Arrays.copyOf(array, array.length);
        int[] res = new int[arr.length];
        AtomicInteger index = new AtomicInteger();
        CountDownLatch latch = new CountDownLatch(arr.length);
        
        for (int val : arr) {
            new Thread(new SleepSort(val, index, res, latch)).start();
        }

        try {
            latch.await();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return res;

    }

    private static class SleepSort implements Runnable {
        private final int val;
        private final AtomicInteger index;
        private final int[] res;
        private final CountDownLatch latch;

        SleepSort(int val, AtomicInteger index, int[] res, CountDownLatch latch) {
            this.val = val;
            this.index = index;
            this.res = res;
            this.latch = latch;
        }

        @Override
        public void run() {
            try {
                Thread.sleep(val);
                res[index.getAndIncrement()] = val;
                latch.countDown();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

猴子排序

原理:一组数字,随机一次,若随机后的结果不是有序的再随机一次。。。直到随机的结果是有序的理论上,只要时间足够长,必定能排好序。就像一组无序的扑克牌,让猴子打乱一次,若还是无序的,猴子再打乱一次。

太慢啊,O(n*n!)

面条排序

原理:将输入分别对应到不同长度的面条上每根面条的长度即为对应的数字的大小。比如,对于[1, 4, 2, 8, 9]这个输入,则分别做出长度为1cm、4cm、2cm、8cm、9cm的面条。然后,将这些面条的一头对齐用手抓住,另一头向下。然后慢慢地将手向下垂直下降第一个触碰到桌面的面条对应的数字则为最大的数字第二个触碰到的就是第二大的,依次类推

Spaghetti排序简直不是一个软件可行的想法 - 它是一种按物理长度排序的“物理”理论方法。基本上它说:“将一堆意大利面条棒推到一个平坦的表面上,使最长的那些比最短的更突出 - 从而按长度”排序“它们

多线程归并排序

一般归并排序

两个线程

分成几个独立的小部分,各个部分让单独的线程去计算。

public static void main(String[] args) {

    int length = 10;
    int[] arr = (new Data(length)).getData();
    System.out.println(Arrays.toString(arr));

    int mid = arr.length >> 1;
    CountDownLatch latch = new CountDownLatch(2);
    new Thread(new Runnable() {

        @Override
        public void run() {
            sort(arr, 0, mid);
            latch.countDown();
        }
    }).start();

    new Thread(new Runnable() {

        @Override
        public void run() {
            sort(arr, mid + 1, arr.length - 1);
            latch.countDown();
        }
    }).start();

    try {
        latch.await();
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
    System.out.println(Arrays.toString(arr));
}

ForkPool/Join 框架

Fork/Join是从JDK 1.7 加入的并发计算框架。

public class MergeSort {
    public static void main(String[] args) {
        int length = 10;
        int[] arr = (new Data(length)).getData();
        System.out.println(Arrays.toString(arr));

        ForkJoinPool pool = new ForkJoinPool();
        pool.invoke(new MergeSortFork(arr));
        System.out.println(Arrays.toString(arr));
    }

    public static class MergeSortFork extends RecursiveAction {
        private static final long serialVersionUID = 425572392953885545L;
        private int[] arr;

        public MergeSortFork(int[] arr) {
            this.arr = arr;
        }
        @Override
        protected void compute() {
            sort(arr,0,arr.length-1);
        }
    }

//下面就是普通归并排序
    public static int[] sort(int[] arr, int l, int r) {
        int mid = ((r - l) >> 1) + l;
        if (l < r) {
            sort(arr, l, mid);
            sort(arr, mid + 1, r);
            merge(arr, l, mid, r);
        }
        return arr;
    }

    public static void merge(int[] arr, int l, int mid, int r) {
        int[] temp = new int[r - l + 1];
        int i = l;
        int j = mid + 1;
        int k = 0;
        while (i <= mid && j <= r) {
            temp[k++] = arr[i] > arr[j] ? arr[j++] : arr[i++];
        }
        while (i <= mid) {
            temp[k++] = arr[i++];
        }
        while (j <= r) {
            temp[k++] = arr[j++];
        }
        for (int x = 0; x < temp.length; x++) {
            arr[l + x] = temp[x];
        }
    }
}

class Data {
    int length;
    int[] arr;

    public Data(int length) {
        this.length = length;
        arr = new int[length];
    }

    public int[] getData() {
        Random random = new Random(System.currentTimeMillis());
        for (int i = 0; i < length; i++) {
            arr[i] = random.nextInt(2 * length);
        }
        return arr;
    }
}

在这里插入图片描述
通过统计可以发现,当待排序序列长度较小时,使用单线程效率要高于多线程,但是随着数量不断增加多线程执行时间越来越接近单线程的执行时间,最终在1000万这个量级开始速率远超单线程。工作中不能滥用多线程,在该使用的时候使用可以加快效率,充分利用多核。但是在不该用的时候使用徒增工作量,有可能效率还不如单线程。

100w数据处理的思想

大量的数据中,寻找最大的k个数,或者是出现次数最多的k个数据,比如说这个数据有10个G放在一个大文件中,电脑内存4G

解题思路就是先把这个文件分块为了确保相同的数据在一个块中,通过计算Hash值来分块相同Hash 放到一个块中

比如每分100个块,这样平均一个块就在100M左右,对每个块分别载入内存最大的前K个数或者出现最多的前K个数据,最后比较这100*K个数据来得到结果。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值