利用多線程對數組進行歸併排序

多線程處理歸併排序的方法通常爲:java

假設有n個線程同步處理,就將數組等分紅n份,每一個線程處理一份,再對最後n個有序數組進行歸併。算法

爲了使對整個算法具備可擴展性,即線程數n能夠自定義,筆者將線程類、處理數組類等進行封裝,分爲最主要的4個類:Array, Merge, MyThread, MoreThreads,代碼以下:數組

/*Array.java*/

import java.util.ArrayList;

/**

 * @author duyue

 *

 *         這個類用來處理數組

 *         

 *         原理:

 *         建立待排序數組成功後,須要配合多線程(假設有n個線程)分別排序,

 *         須要將數組儘可能等分紅n個分數組(保存到列表中),由n個線程分別歸

 *         並排序,並將各個有序數組(再次保存到列表中),最後整合(不歸併

 *         整合)並覆蓋原數組,等待最後歸併。

 */

class Array {

    /**

     * 構造一個保存數組的列表,用於保存分割後的分數組

     */

    static ArrayList<int[]> arrayList = new ArrayList<int[]>();

    /**

     * @param length 數組長度

     * @return 待排序的數組

     */

    static int[] createArray(int length) {

        int[] array = new int[length];

        for (int i = 0; i < length; i++) {

            array[i] = (int) (Math.random() * 10000);

        }

        return array;

    }

    /**

     * @param array 待分割(多線程排序須要)的數組

     * @param num   線程數,即要分割的份數

     */

    static void divideArray(int[] array, int num) {

        int k = 0;                                  //記錄原數組的複製進度,k表明當前數組的複製初始點

        for (int i = 0; i < num; i++) {

            int point = array.length / num;         //分數組的長度

            int[] a = new int[0];                   //保存分數組

            /*考慮到不夠整除的狀況,將剩餘的項所有放在最後一個分數組中*/

            if (i != num - 1) a = new int[point];

            if (i == num - 1) a = new int[array.length - k];

            /*將array[k, k + a.length -1]複製到a[0, a.length]中*/

            System.arraycopy(array, k, a, 0, a.length);

            arrayList.add(a);                       //把獲得的分數組保存在列表中

            k += point;                             //移動複製初始點

        }

    }

    /**

     * @param newArray 由有序分數組整合(不歸併)的新數組

     * @param num      有序分數組的個數,即由num個線程分別排序後獲得的數組數,也就是線程數

     */

    static void newArray(int[] newArray, int num) {

        /*原理與divideArray方法類似*/

        int k = 0;                                  //記錄新數組的待整合初始點

        /*把列表元素(即數組)逐個複製到新的數組中*/

        for (int i = 0; i < num; i++) {

            System.arraycopy(arrayList.get(i), 0, newArray, k, arrayList.get(i).length);

            k += arrayList.get(i).length;           //移動待整合初始點

        }

    }

}
/*Merge.java*/

/**

 * @author duyue

 *         

 *         這是對數組進行歸併排序的類

 */

class Merge {

    private int[] temp;                 //暫時存放待排序數組的temp數組

    /**

     * @param a 待排序的數組由構造器傳遞到類中

     */

    Merge(int[] a) {

        temp = new int[a.length];

    }

    public void sort(int[] a) {

        sort(a, 0, a.length - 1);

    }

    public void sort(int[] a, int low, int high) {

        if (low >= high)

            return;

        int mid = low + (high - low) / 2;

        sort(a, low, mid);              //將左半邊排序

        sort(a, mid + 1, high);         //將左半邊排序

        merge(a, low, mid, high);       //歸併結果

    }

    /**

     * @param a 待歸併的數組,其中a[low,mid]和a[mid+1,high]都是有序的

     */

    public void merge(int[] a, int low, int mid, int high) {

        int i = low, j = mid + 1;

        /*將a[low,high]複製到temp[low,high]*/

        System.arraycopy(a, low, temp, low, high - low + 1);

        /*歸併到a[low,high]*/

        for (int k = low; k <= high; k++) {

            if (i > mid)

                a[k] = temp[j++];

            else if (j > high)

                a[k] = temp[i++];

            else if (temp[j] < temp[i])

                a[k] = temp[j++];

            else

                a[k] = temp[i++];

        }

    }

}
/*MyThread.java*/

import java.util.concurrent.CountDownLatch;

/**

 * @author duyue

 *         

 *         這個類用來定義線程,使其可以對數組進行歸併排序處理

 */

class MyThread extends Thread {

    public int[] aux;                   //定義一個數組,用來保存待處理的數組

    private CountDownLatch latch;       //定義這個類用來等待各個線程都完成工做,再進行下一步操做

    /*經過構造器將待處理的數組傳遞到線程的類中*/

    public MyThread(int[] aux, CountDownLatch latch) {

        this.aux = aux;

        this.latch = latch;

    }

    public void run() {

        Merge mergeThread = new Merge(aux);

        mergeThread.sort(aux);

        latch.countDown();

    }

}
/*MoreThreads.java*/

import java.util.ArrayList;

import java.util.concurrent.CountDownLatch;

/**

 * @author duyue

 *         

 *         本類是多線程處理歸併排序的核心部分。

 *         

 *         原理:

 *         由用戶指定線程數,例如n個線程,將數組分爲n份,分別用n個線程對這n個數組進行歸併排序,

 *         獲得n個有序分數組,再對這n個有序數組歸併就得出最後的結果。

 *         線程數越多,相應的速度就會越快。

 *         要處理的數組長度越長,多線程與單線程的對比就越大。

 */

class MoreThreads {

    /**

     * @param num 線程數,由用戶定義

     */

    MoreThreads(int num) {

        System.out.println("如今是" + num + "個線程處理歸併排序:");

        int length = 100;                   //數組總長度

        for (int j = 0; j < 6; j++) {

            /*記錄起始時間*/

            long beginTime = System.currentTimeMillis();

            /*建立待排序的數組*/

            int[] myArray = Array.createArray(length);

            /*將數組近乎等分紅num份,以便利用多線程對各個數組排序*/

            Array.divideArray(myArray, num);

            /*

             * 對各個數組利用num個線程同步排序。

             * 將num個線程保存在列表threads中,方便將各個線程處理後的數組調出。

             * CountDownLatch類用於等待全部的線程都工做完成後,進行最終的歸併。

             */

            ArrayList<MyThread> threads = new ArrayList<MyThread>();

            CountDownLatch latch = new CountDownLatch(num);

            for (int i = 0; i < num; i++) {

                MyThread thread = new MyThread(Array.arrayList.get(i), latch);

                thread.start();

                threads.add(thread);

            }

            try {

                latch.await();

            } catch (InterruptedException e) {

                e.printStackTrace();

            }

            /*

             * 清空原裝有數組列表中的元素,

             * 將排序後的各個數組從threads列表中調出,添加到數組列表Array中

             */

            Array.arrayList.clear();

            for (int i = 0; i < num; i++) {

                Array.arrayList.add(threads.get(i).aux);

            }

            /*把各個排序後數組規整到長數組中,並對三個有序數組歸併到一個數組中*/

            Array.newArray(myArray, num);

            /*

             * 對有序數組進行歸併

             * 歸併原理:

             * 將第一個有序分數組(即第一個線程排序後的數組)與其下一個有序分數組(即第二個線程

             * 排序後的數組)歸併成一個數組,再把歸併的數組與其下一個有序分數組(即第三個線程排

             * 序後的數組)歸併,依此類推.

             */

            int low = 0;                    //整合後的長數組myArray的首位

            int mid = -1;                   //待歸併的第一個有序分數組的末位

            int high;                       //待歸併的第二個有序分數組的末位

            for (int i = 0; i < num - 1; i++) {

                Merge merge = new Merge(myArray);

                mid = Array.arrayList.get(i).length + mid;

                high = mid + Array.arrayList.get(i + 1).length;

                merge.merge(myArray, low, mid, high);

            }

            /*記錄結束時間*/

            long endTime = System.currentTimeMillis();

            System.out.println(length + "項數組歸併排序的時間:" + (endTime - beginTime) + "ms");

            length = length * 10;

            Array.arrayList.clear();        //清空列表內容,對下一次循環不形成影響

        }

    }

}

運行如下代碼便可測試:多線程

/*TestThread*/

import java.util.Scanner;

/**

 * @author duyue

 *         

 *         這是一個測試類,用於展現結果。

 */

public class TestThread {

    public static void main(String[] args) {

        new MoreThreads(1);

        System.out.println("--------------------------------");

        new MoreThreads(2);

        System.out.println("--------------------------------");

        new MoreThreads(3);

        System.out.println("--------------------------------");

        System.out.println("你還想嘗試更多線程處理歸併排序嗎?(y:yes, n:no)");

        while (true) {

            Scanner in = new Scanner(System.in);

            String s = in.nextLine();

            if (s.equals("n")) {

                System.out.println("byebye!");

                in.close();

                break;

            } else if (s.equals("y")) {

                System.out.println("請輸入要嘗試的線程數:");

                new MoreThreads(in.nextInt());

                System.out.println("--------------------------------");

                System.out.println("你還想嘗試更多線程處理歸併排序嗎?(y:yes, n:no)");

            } else

                System.out.println("輸入錯誤!請從新輸入");

        }

    }

}
相關文章
相關標籤/搜索