好的。根据您在回复我的评论时所说的话,这是我将尝试的。
我希望能够说“第 4 到第 6”并得到类似:3、2、1(未排序,但都小于正确的第 4 个元素);4, 5, 6(已排序并且在相同的位置,它们将用于排序列表);8, 7, 9(未排序,但都大于正确的第 6 个元素)。
让我们将 10 添加到我们的列表中以使其更容易:10、9、8、7、6、5、4、3、2、1。
所以,你可以做的是使用快速选择算法来找到第 i个和第 k个元素。在您上面的情况下,i 是 4,k 是 6。这当然会返回值 4 和 6。这将需要两次通过您的列表。所以,到目前为止,运行时间是 O(2n) = O(n)。当然,下一部分很简单。我们关心的数据有上限和下限。我们需要做的就是再次遍历我们的列表,寻找在我们的上限和下限之间的任何元素。如果我们找到这样的元素,我们会将其放入一个新的列表中。最后,我们对仅包含我们关心的第 i个到第 k个元素的 List 进行排序。
所以,我相信总运行时间最终是 O(N) + O((ki)lg(ki))
static void Main(string[] args) {
//create an array of 10 million items that are randomly ordered
var list = Enumerable.Range(1, 10000000).OrderBy(x => Guid.NewGuid()).ToList();
var sw = Stopwatch.StartNew();
var slowOrder = list.OrderBy(x => x).Skip(10).Take(10).ToList();
sw.Stop();
Console.WriteLine(sw.ElapsedMilliseconds);
//Took ~8 seconds on my machine
sw.Restart();
var smallVal = Quickselect(list, 11);
var largeVal = Quickselect(list, 20);
var elements = list.Where(el => el >= smallVal && el <= largeVal).OrderBy(el => el);
Console.WriteLine(sw.ElapsedMilliseconds);
//Took ~1 second on my machine
}
public static T Quickselect<T>(IList<T> list , int k) where T : IComparable {
Random rand = new Random();
int r = rand.Next(0, list.Count);
T pivot = list[r];
List<T> smaller = new List<T>();
List<T> larger = new List<T>();
foreach (T element in list) {
var comparison = element.CompareTo(pivot);
if (comparison == -1) {
smaller.Add(element);
}
else if (comparison == 1) {
larger.Add(element);
}
}
if (k <= smaller.Count) {
return Quickselect(smaller, k);
}
else if (k > list.Count - larger.Count) {
return Quickselect(larger, k - (list.Count - larger.Count));
}
else {
return pivot;
}
}