为N个已排序的文件实现N路合并的最佳方法是什么?
假设我有9个已排序的文件,每个文件有10个记录?如何合并这些文件以创建包含90个已排序记录的大文件?
答案 0 :(得分:6)
我假设您的示例中可能会提供更多数据。如果您可以同时打开所有文件,则可以使用此算法:
请注意,您不必一次将所有文件都读入内存,因此如果您有大量合适的大文件,这将很有效,但如果您有大量小文件则不行。
如果你有很多小文件,你应该将它们组合在一起,为每个组制作一个输出文件,然后重复这个过程来合并这些新组。
在C#中,您可以使用例如SortedDictionary
来实现优先级队列。
答案 1 :(得分:5)
解决其他答案中的评论:
如果你有一个可变数量的文件,这就是我要做的。这只是一个草图,以实现这个想法;这段代码没有编译,我的方法名称错了,等等。
// 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 }
这有意义吗?您只需继续从优先级队列中抓取最小的东西,并将其替换为该流中的下一条记录(如果有的话)。最终队列将为空,您将完成。
答案 2 :(得分:0)
策略可能取决于数据量。
这是一个代码示例,它读入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;
}
}
答案 3 :(得分:0)
我会说不使用优先级队列,不要使用IEnumerable。两者都很慢。
以下是对外部存储器中的已排序文件进行排序或合并的快速方法:
http://www.codeproject.com/KB/recipes/fast_external_sort.aspx