排序算法---外部排序

算法设计:

可以使用外部排序对大容量数据进行排序。数据量过大,存储于外部文件,数据不能完全加载到内存中。
1.从文件中读取一固定数目的数据到数组,对数组排序,将排好序的数组作为一个数据段输出到一个临时文件,重复上述过程,直到源文件中数据读取完
2.将存储了数据段的文件中一般的数据段复制到一个临时文件,然后对着两个文件的数据段归并排序,每两个数据段归并形成一个大的数据段存储于一个文件中,重复2,直到只有一个数据段为止。
代码实现:
创建存储大量数据的文件:
public class CreateLargeFile {

    public static void main(String[] args) throws IOException {
        DataOutputStream output = new DataOutputStream(
                new BufferedOutputStream(new FileOutputStream("D:\\largedata.dat")));
        for (int i = 0; i < 800004; i++)
            output.writeInt((int) (Math.random() * 1000000));
        output.close();

        DataInputStream input = new DataInputStream(new BufferedInputStream(new FileInputStream("D:\\largedata.dat")));
        for (int i = 0; i < 100; i++)
            System.out.print(input.readInt() + " ");
        input.close();
    }
}

排序实现:

public class SortLargeFile {
    public static final int MAX_ARRAY_SIZE = 100000;
    public static final int BUFFER_SIZE = 100000;

    public static void main(String[] args) throws IOException {
        // 将largedata.dat中的数据排序存入sortedfile.dat中
        sort("D:\\largedata.dat", "D:\\sortedfile.dat");
        // 输出排序后的前100个数
        displayFile("D:\\sortedfile.dat");
    }

    /**
     * 将源文件数据排序存入目标文件
     * 
     * @param sourcefile
     * @param targetfile
     * @throws IOException
     */
    public static void sort(String sourcefile, String targetfile) throws IOException {
        int numberOfSegments = initializeSegments(MAX_ARRAY_SIZE, sourcefile, "D:\\f1.dat");
        merge(numberOfSegments, MAX_ARRAY_SIZE, "D:\\f1.dat", "D:\\f2.dat", "D:\\f3.dat", targetfile);
    }

    /**
     * 将原始文件排序为一个个有顺序的段,存入临时文件
     * 
     * @param segmentSize
     * @param originalFile
     * @param f1
     * @return
     * @throws IOException
     */
    private static int initializeSegments(int segmentSize, String originalFile, String f1) throws IOException {
        int[] list = new int[segmentSize];
        DataInputStream input = new DataInputStream(new BufferedInputStream(new FileInputStream(originalFile)));
        DataOutputStream output = new DataOutputStream(new BufferedOutputStream(new FileOutputStream(f1)));

        int numberOfSegments = 0;
        while (input.available() > 0) {
            numberOfSegments++;
            int i = 0;
            for (; input.available() > 0 && i < segmentSize; i++) {
                list[i] = input.readInt();
            }
            // 将读出的数据排序
            Arrays.sort(list, 0, i);
            // 将排序后的数据输入临时文件
            for (int j = 0; j < i; j++) {
                output.writeInt(list[j]);
            }
        }
        input.close();
        output.close();

        return numberOfSegments;
    }

    /**
     * 将排好序的段归并并排序直到只有一个段
     * 
     * @param numberOfSegments
     * @param segmentSize
     * @param f1
     * @param f2
     * @param f3
     * @param targetfile
     * @throws IOException
     */
    private static void merge(int numberOfSegments, int segmentSize, String f1, String f2, String f3, String targetfile)
            throws IOException {
        if (numberOfSegments > 1) {
            mergeOneStep(numberOfSegments, segmentSize, f1, f2, f3);
            merge((numberOfSegments + 1) / 2, segmentSize * 2, f3, f1, f2, targetfile);
        } else {
            File sortedFile = new File(targetfile);
            if (sortedFile.exists())
                sortedFile.delete();
            new File(f1).renameTo(sortedFile);
        }
    }

    /**
     * 归并第一阶段
     * 
     * @param numberOfSegments
     * @param segmentSize
     * @param f1
     * @param f2
     * @param f3
     * @throws IOException
     */
    private static void mergeOneStep(int numberOfSegments, int segmentSize, String f1, String f2, String f3)
            throws IOException {
        DataInputStream f1Input = new DataInputStream(new BufferedInputStream(new FileInputStream(f1), BUFFER_SIZE));
        DataOutputStream f2Output = new DataOutputStream(
                new BufferedOutputStream(new FileOutputStream(f2), BUFFER_SIZE));
        // 将f1中的段复制一半到f2中
        copyHalfToF2(numberOfSegments, segmentSize, f1Input, f2Output);
        f2Output.close();

        // 将f1和f2中的段归并为更大的段到f3中
        DataInputStream f2Input = new DataInputStream(new BufferedInputStream(new FileInputStream(f2), BUFFER_SIZE));
        DataOutputStream f3Output = new DataOutputStream(
                new BufferedOutputStream(new FileOutputStream(f3), BUFFER_SIZE));
        mergeSegments(numberOfSegments / 2, segmentSize, f1Input, f2Input, f3Output);

        f1Input.close();
        f2Input.close();
        f3Output.close();
    }

    /**
     * 复制前半部分的段
     * 
     * @param numberOfSegments
     * @param segmentSize
     * @param f1Input
     * @param f2Output
     * @throws IOException
     */
    private static void copyHalfToF2(int numberOfSegments, int segmentSize, DataInputStream f1Input,
            DataOutputStream f2Output) throws IOException {
        for (int i = 0; i < (numberOfSegments / 2) * segmentSize && f1Input.available() > 0; i++) {
            f2Output.writeInt(f1Input.readInt());
        }
    }

    /**
     * 归并所有的段
     * 
     * @param numberOfSegments
     * @param segmentSize
     * @param f1Input
     * @param f2Input
     * @param f3Output
     * @throws IOException
     */
    private static void mergeSegments(int numberOfSegments, int segmentSize, DataInputStream f1Input,
            DataInputStream f2Input, DataOutputStream f3Output) throws IOException {
        for (int i = 0; i < numberOfSegments; i++) {
            mergeTwoSegments(segmentSize, f1Input, f2Input, f3Output);
        }
        // 如果f1中还有剩余的则复制到f3
        while (f1Input.available() > 0) {
            f3Output.writeInt(f1Input.readInt());
        }
    }

    /**
     * 归并两个分段
     * 
     * @param segmentSize
     * @param f1
     * @param f2
     * @param f3
     * @throws IOException
     */
    private static void mergeTwoSegments(int segmentSize, DataInputStream f1, DataInputStream f2, DataOutputStream f3)
            throws IOException {
        int intFromF1 = f1.readInt();
        int intFromF2 = f2.readInt();
        int f1Count = 1;
        int f2Count = 1;

        while (true) {
            if (intFromF1 < intFromF2) {
                f3.writeInt(intFromF1);
                if (f1.available() == 0 || f1Count++ >= segmentSize) {
                    f3.write(intFromF2);
                    break;
                } else
                    intFromF1 = f1.readInt();
            } else {
                f3.writeInt(intFromF2);
                if (f2.available() == 0 || f2Count++ >= segmentSize) {
                    f3.write(intFromF1);
                    break;
                } else
                    intFromF2 = f2.readInt();
            }
        }

        while (f1.available() > 0 && f1Count++ < segmentSize) {
            f3.write(f1.readInt());
        }

        while (f2.available() > 0 && f2Count++ < segmentSize) {
            f3.write(f2.readInt());
        }
    }

    public static void displayFile(String fileName) {
        try {
            DataInputStream input = new DataInputStream(new FileInputStream(fileName));
            for (int i = 0; i < 100; i++) {
                System.out.print(input.readInt() + " ");
            }
            input.close();
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }
}

你可能感兴趣的:(数据结构与算法)