如何实现N路归并以合并N个有序文件?
假设我有9个每个都有10条记录的有序文件,我该如何将这些文件合并成一个90个有序记录的大文件?
如何实现N路归并以合并N个有序文件?
假设我有9个每个都有10条记录的有序文件,我该如何将这些文件合并成一个90个有序记录的大文件?
回答中提到的问题:
如果你有一个可变数量的文件,这是我要做的。这只是为了让大家理解的草图;这段代码不能编译,我的方法名写错了等等。
// initialize the data structures
var priorityQueue = new SortedDictionary<Record, Stream>();
var streams = new List<Stream>();
var outStream = null;
try
{
// open the streams.
outStream = OpenOutputStream();
foreach(var filename in filenames)
streams.Add(GetFileStream(filename));
// initialize the priority queue
foreach(var stream in streams)
{
var record = ReadRecord(stream);
if (record != null)
priorityQueue.Add(record, stream);
// the main loop
while(!priorityQueue.IsEmpty)
{
var record = priorityQueue.Smallest;
var smallestStream = priorityQueue[record];
WriteRecord(record, outStream);
priorityQueue.Remove(record);
var newRecord = ReadRecord(smallestStream);
if (newRecord != null)
priorityQueue.Add(newRecord, smallestStream);
}
}
finally { clean up the streams }
这样说是否清晰明了?你只需不断从优先队列中取出最小的元素,并用该流中的下一个记录替换它(如果有的话)。最终,队列将为空,你就完成了。
SortedDictionary
来实现优先队列。策略可能取决于数据量。
这里有一个代码示例,它读取 N 个已排序的文本文件并将它们合并。我没有包括重复检查,但应该很容易实现。
首先是一个帮助类。
class MergeFile : IEnumerator<string>
{
private readonly StreamReader _reader;
public MergeFile(string file)
{
_reader = File.OpenText(file);
Current = _reader.ReadLine();
}
public string Current { get; set; }
public void Dispose()
{
_reader.Close();
}
public bool MoveNext()
{
Current = _reader.ReadLine();
return Current != null;
}
public void Reset()
{
throw new NotImplementedException();
}
object IEnumerator.Current
{
get { return Current; }
}
}
然后编写代码来读取和合并(在生产中应进行重构以提高清晰度):
// Get the file names and instantiate our helper class
List<IEnumerator<string>> files = Directory.GetFiles(@"C:\temp\files", "*.txt").Select(file => new MergeFile(file)).Cast<IEnumerator<string>>().ToList();
List<string> result = new List<string>();
IEnumerator<string> next = null;
while (true)
{
bool done = true;
// loop over the helpers
foreach (var mergeFile in files)
{
done = false;
if (next == null || string.Compare(mergeFile.Current, next.Current) < 1)
{
next = mergeFile;
}
}
if (done) break;
result.Add(next.Current);
if (!next.MoveNext())
{
// file is exhausted, dispose and remove from list
next.Dispose();
files.Remove(next);
next = null;
}
}
我会建议不要使用优先队列,也不要使用IEnumerable。这两个都非常慢。
以下是一种在外部存储器中快速排序或合并已排序文件的方法:
http://www.codeproject.com/KB/recipes/fast_external_sort.aspx