1 回答

TA貢獻(xiàn)1845條經(jīng)驗(yàn) 獲得超8個(gè)贊
好吧,讓我們從唾手可得的果實(shí)開(kāi)始:
您的實(shí)施是錯(cuò)誤的。您需要index + 1從序列中獲取元素。要理解這一點(diǎn),請(qǐng)考慮index = 0并重新閱讀Take.
您的“基準(zhǔn)比較”之所以有效,是因?yàn)檎{(diào)用OrderBy()IEnumerable 不會(huì)修改基礎(chǔ)集合。對(duì)于我們要做的事情,只允許修改底層數(shù)組會(huì)更容易。因此,我冒昧地更改您的代碼以在每次迭代中從頭開(kāi)始生成值。
另外Take(i + 1).Last()相當(dāng)于ElementAt(i). 你真的應(yīng)該使用它。
哦,你的基準(zhǔn)真的沒(méi)有用,因?yàn)槟阈枰牡姆秶鷥?nèi)的元素Take越多,這些算法就越接近彼此。據(jù)我所知,您對(duì) O(n log n) 的運(yùn)行時(shí)分析是正確的。
有一個(gè)解決方案的時(shí)間復(fù)雜度為 O(n)(不是我之前錯(cuò)誤聲明的 O(log n))。這是面試官期待的解決方案。
不管它值多少錢(qián),您在那里編寫(xiě)的代碼都不能移動(dòng)到該解決方案,因?yàn)槟鷮?duì)排序過(guò)程沒(méi)有任何控制權(quán)。
如果你有,你可以實(shí)施Quickselect,(這是這里的目標(biāo)),從而在理論上改進(jìn)你在這里提出的 LINQ 查詢(特別是對(duì)于高索引)。以下是基于您的代碼對(duì) quickselect 維基百科文章中的偽代碼的翻譯
static T SelectK<T>(T[] values, int left, int right, int index)
where T : IComparable<T>
{
if (left == right) { return values[left]; }
// could select pivot deterministically through median of 3 or something
var pivotIndex = rand.Next(left, right + 1);
pivotIndex = Partition(values, left, right, pivotIndex);
if (index == pivotIndex) {
return values[index];
} else if (index < pivotIndex) {
return SelectK(values, left, pivotIndex - 1, index);
} else {
return SelectK(values, pivotIndex + 1, right, index);
}
}
static int Partition<T>(T[] values, int left, int right, int pivot)
where T : IComparable<T>
{
var pivotValue = values[pivot];
Swap(values, pivot, right);
var storeIndex = left;
for (var i = left; i < right; i++) {
if (values[i].CompareTo(pivotValue) < 0)
{
Swap(values, storeIndex, i);
storeIndex++;
}
}
Swap(values, right, storeIndex);
return storeIndex;
}
我運(yùn)行的測(cè)試的非代表性子樣本給出了輸出:
i: 6724, OrderBy: 52365, value 6723
i: 6724, SelectK: 40014, value 6724
i: 395, OrderBy: 14436, value 394
i: 395, SelectK: 26106, value 395
i: 7933, OrderBy: 32523, value 7932
i: 7933, SelectK: 17712, value 7933
i: 6730, OrderBy: 46076, value 6729
i: 6730, SelectK: 34367, value 6730
i: 6536, OrderBy: 53458, value 6535
i: 6536, SelectK: 18341, value 6536
由于我的 SelectK 實(shí)現(xiàn)使用隨機(jī)樞軸元素,因此它的輸出有相當(dāng)多的變化(例如參見(jiàn)第二次運(yùn)行)。它也比標(biāo)準(zhǔn)庫(kù)中實(shí)現(xiàn)的高度優(yōu)化的排序算法差得多。
即使那樣,也有 SelectK 直接優(yōu)于標(biāo)準(zhǔn)庫(kù)的情況,即使我沒(méi)有付出太多努力。
現(xiàn)在用中位數(shù) 3 [1]替換隨機(jī)主元(這是一個(gè)非常糟糕的主元選擇器),我們可以獲得稍微不同的 SelectK 并與 OrderBy 和 SelectK 競(jìng)爭(zhēng)。
我一直在用數(shù)組中的 1m 個(gè)元素與這三匹馬賽跑,使用你已經(jīng)擁有的隨機(jī)排序,在數(shù)組的最后 20% 中請(qǐng)求一個(gè)索引,并得到如下結(jié)果:
Winning counts: OrderBy 32, SelectK 32, MedianOf3 35
Winning counts: OrderBy 26, SelectK 35, MedianOf3 38
Winning counts: OrderBy 25, SelectK 41, MedianOf3 33
即使對(duì)于 100k 元素并且不將索引限制在數(shù)組的末尾,這種模式似乎仍然存在,盡管沒(méi)有那么明顯:
--- 100k elements
Winning counts: OrderBy 24, SelectK 34, MedianOf3 41
Winning counts: OrderBy 33, SelectK 33, MedianOf3 33
Winning counts: OrderBy 32, SelectK 38, MedianOf3 29
--- 1m elements
Winning counts: OrderBy 27, SelectK 32, MedianOf3 40
Winning counts: OrderBy 32, SelectK 38, MedianOf3 29
Winning counts: OrderBy 35, SelectK 31, MedianOf3 33
一般來(lái)說(shuō),草率實(shí)施的 quickselect 在平均情況下有三分之二的時(shí)間優(yōu)于您的建議......我想說(shuō)這是一個(gè)非常強(qiáng)大的指標(biāo),如果您想了解細(xì)節(jié),這是更好的算法。
當(dāng)然,您的實(shí)現(xiàn)更容易理解 :)
[1] - 取自這個(gè) SO answer的實(shí)現(xiàn),每個(gè)遞歸深度步驟進(jìn)行 3 次比較
- 1 回答
- 0 關(guān)注
- 160 瀏覽
添加回答
舉報(bào)