笔试题
下面是来自于百度的编程笔试题,感兴趣的同学可以先自己想想解题思路:
你现在有一个文件,文件中顺序存有N个记录,R1,R2,...,RN,这些记录不是有序的,但是你知道一个整数M, 这些记录满足R1<R2<...<RM以及RM+1<RM+2<...RN.
1. 设计一个算法或编写一个程序,将文件中的记录排序为R1',R2',...,RN',算法或程序读取文件的次数为O(N),不限内存使用;
2. 设计一个算法或编写一个程序,将文件中的记录排序为R1',R2',...,RN',算法或程序读写文件的次数为O(N),空间复杂度为O(1),亦即,你使用的内存大小和M,N均无关。
--------------我是华丽丽的分隔线----------------
2 内排序和外排序
根据在排序过程中待排序的记录是否全部被放置在内存中,排序可以分为内排序和外排序。
内排序是在排序整个过程中,待排序的所有记录全部被放置在内存中。外排序是由于排序的记录个数太多,不能同时放置在内存,整个排序过程需要在内外存之间多次交换数据才能进行。
3 排序算法的时间性能
排序算法的时间开销是衡量其好坏的最重要的标志。
在内排序中,主要进行两种操作:比较和移动。内排序算法的时间复杂度是比较次数加上移动次数的总和;
在外排序中,除了比较和移动,还有读写文件的操作。外排序算法的时间复杂度是比较次数,移动次数以及读写文件次数的总和。
高效率的排序算法应该是具有尽可能少的比较次数,移动次数及读写文件的次数。
4 排序算法的空间性能
评价排序算法的另一个主要标准是执行算法所需要的辅助存储空间。辅助存储空间是除了存放待排序所占用的存储空间之外,执行算法所需要的其他存储空间。
5 回到笔试题
基础知识介绍完毕,如果大家还有不明白的地方,可以先找找相关的资料参考一下。
5.1 对题目的理解
要解决一个问题,首先需要明确问题:
初始条件下,有N个数据数据都存放在一个文件中,并且有一个整数M,使得这些记录满足R1<R2<...<RM以及RM+1<RM+2<...RN。举一个例子来说,对于以下10(N=10)个数(2,3,4,7,10,1,5,6,8,9),当M是2时,分成两个序列(2,3)和(4,7,10,1,5,6,8,9),由于第二个序列不是依次增大的,所以不满足条件。当M是5时,分成两个序列(2,3,4,7,10)和(1,5,6,8,9)都是依次增大,因此满足条件。
然后我们需要将这些记录排序,其实就是合并这两个子序列。以例子来看,其实就是需要把(2,3,4,7,10)和(1,5,6,8,9)两个有序序列合并成(1,2,3,4,5,6,7,8,9,10)一个有序序列。细心的同学可能会想起归并排序。是的,这个合并过程其实是归并排序中的一个子步骤。
题目的要求,第一个要求是读取文件次数为O(N),但是不限制内存的使用;第二个要求是读取文件次数为O(N),但是空间复杂度为O(1)。区别在于限不限制内存的使用。
如果不限制内存的使用,我们可以将文件中的内容全部读取到内存中,保存为数组,然后再对数组进行排序;如果限制内存的使用,我们可以采用外排序来解决这个问题,外排序一般会借助于文本文件。
最后的实现代码(java)如下:
/**
* Sample code
*/
public class FileRecordSortMain {
final static String SOURCE_FILE_NAME = "file.txt";
final static String TEMP_FILE_NAME1 = "file_temp1.txt";
final static String TEMP_FILE_NAME2 = "file_temp2.txt";
final static int N = 10;
final static int M = 5;
/**
* 第一种方法:
* 将原文件中的记录顺序读取到内存中,以数组保存,然后以M为分隔将
* 数组看为两组,互相比较两组元素之后输出,最后输出剩下的元素。
*
* 性能分析:
* 1. 时间复杂度是O(N),因为读取原文件N次,比较小于4N次,输出N次,
* 加在一起的数量级是N的一次方
* 2. 空间复杂度是N,因为将原文件中的记录事先读取到内存中,以数组
* 保存,这个辅助空间的长度是N
*/
static void sortInManner1() {
//1. 将原文件中的记录顺序读取到内存中,以数组保存
BufferedReader reader = getFileReader(SOURCE_FILE_NAME);
int[] arry = new int[N];
String str = "";
int index = 0;
try {
while ((str=reader.readLine()) != null) {
arry[index++] = Integer.valueOf(str);
}
} catch (IOException e) {
e.printStackTrace();
}
//2. 将index<M数组元素看为一组,将index>=M的数组元素看为另一组
//3. 互相比较两组元素,输出较小者,然后将较小者所在组的index+1
int i = 0;
int j = M;
System.out.println("Sort Manner 1 Result:");
while (i<M && j<N) {
if (arry[i] < arry[j]) {
System.out.print(arry[i++] + " ");
} else {
System.out.print(arry[j++] + " ");
}
}
//4. 输出比较之后剩下来的元素
while (i < M) {
System.out.print(arry[i++] + " ");
}
while (j < N) {
System.out.print(arry[j++] + " ");
}
System.out.println();
}
/**
* 第二种方法:
* 将原文件中的记录以M分隔,读取后保存到两个临时文件file_temp1.txt
* 和file_temp2.txt中,然后将这两个临时文件中保存的数组看为两组,
* 分别从这两个临时文件中读取元素,比较后输出,最后读取并输出剩下
* 的元素。
*
* 性能分析:
* 1. 时间复杂度是O(N),因为读取原文件N次,写临时文件N次,读取临时
* 文件N次,比较小于6N次,输出N次,加在一起是N的一次方
* 2. 空间复杂度是1(与M,N均无关系),因为:
* 在步骤1中,使用1个String和1个int作为辅助空间
* 在步骤2,3,4中,使用1个String和1个String作为辅助空间,加在一起
* 与M,N均无关系
*/
static void sortInManner2() {
//1. 将原文件中的记录以M分隔,读取后保存到两个临时文件
//file_temp1.txt和file_temp2.txt中
BufferedReader sourceReader = getFileReader(SOURCE_FILE_NAME);
BufferedWriter writer1 = getFileWriter(TEMP_FILE_NAME1);
BufferedWriter writer2 = getFileWriter(TEMP_FILE_NAME2);
String str = "";
int index = 0;
try {
while ((str=sourceReader.readLine()) != null) {
if (index < M) {
writer1.write(str+"\n");
} else {
writer2.write(str+"\n");
}
++index;
}
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
writer1.close();
writer2.close();
} catch (IOException e) {
e.printStackTrace();
}
}
//2. 将file_temp1.txt中的元素看为一组,将file_temp1.txt中的元
//素看为另一组
//3. 互相比较两组元素,输出较小者,然后继续从较小者所在的临时
//文件中读取元素
BufferedReader tempReader1 = getFileReader(TEMP_FILE_NAME1);
BufferedReader tempReader2 = getFileReader(TEMP_FILE_NAME2);
String numTemp1 = "";
String numTemp2 = "";
try {
numTemp1 = tempReader1.readLine();
numTemp2 = tempReader2.readLine();
System.out.println("Sort Manner 2 Result:");
while (numTemp1!=null && numTemp2!=null) {
if (Integer.valueOf(numTemp1) <
Integer.valueOf(numTemp2)) {
System.out.print(numTemp1 + " ");
numTemp1 = tempReader1.readLine();
} else {
System.out.print(numTemp2 + " ");
numTemp2 = tempReader2.readLine();
}
}
//4. 输出比较之后剩下来的元素
while (numTemp1 != null) {
System.out.print(numTemp1 + " ");
numTemp1=tempReader1.readLine();
}
while (numTemp2 != null) {
System.out.print(numTemp2 + " ");
numTemp2=tempReader2.readLine();
}
} catch (IOException e) {
e.printStackTrace();
}
System.out.println();
}
public static void main(String[] args) {
sortInManner1();
sortInManner2();
}
static BufferedReader getFileReader(String filename) {
String souceFileAbsoluteName = getAbsolutePathName() +
File.separator + filename;
InputStream is = null;
BufferedReader reader = null;
try {
is = new FileInputStream(souceFileAbsoluteName);
reader = new BufferedReader(new InputStreamReader(is));
} catch (FileNotFoundException e) {
e.printStackTrace();
}
return reader;
}
static BufferedWriter getFileWriter(String filename) {
String souceFileAbsoluteName = getAbsolutePathName() +
File.separator + filename;
OutputStream os = null;
BufferedWriter writer = null;
try {
os = new FileOutputStream(souceFileAbsoluteName);
writer = new BufferedWriter(new OutputStreamWriter(os));
} catch (FileNotFoundException e) {
e.printStackTrace();
}
return writer;
}
static String getAbsolutePathName() {
File directory = new File("");
return directory.getAbsolutePath();
}
}