我“发明了”“新”排序算法。好吧,我明白我不能发明好东西,所以我试图在维基百科上搜索它,但所有排序算法似乎都不是我的。所以我有三个问题:
所以,我的算法的想法:如果我们有一个数组,我们可以计算已排序元素的数量,如果这个数字少于长度的一半,我们可以反转数组,使其更加有序。之后我们可以对数组的前半部分和后半部分进行排序。在最好的情况下,我们只需要O(n) - 如果数组完全按好或坏的方向排序。我在评估平均时间和最差时间复杂度方面存在一些问题。
C#上的代码:
public static void Reverse(int[] array, int begin, int end) {
int length = end - begin;
for (int i = 0; i < length / 2; i++)
Algorithms.Swap(ref array[begin+i], ref array[begin + length - i - 1]);
}
public static bool ReverseIf(int[] array, int begin, int end) {
int countSorted = 1;
for (int i = begin + 1; i < end; i++)
if (array[i - 1] <= array[i])
countSorted++;
int length = end - begin;
if (countSorted <= length/2)
Reverse(array, begin, end);
if (countSorted == 1 || countSorted == (end - begin))
return true;
else
return false;
}
public static void ReverseSort(int[] array, int begin, int end) {
if (begin == end || begin == end + 1)
return;
// if we use if-operator (not while), then array {2,3,1} transforms in array {2,1,3} and algorithm stop
while (!ReverseIf(array, begin, end)) {
int pivot = begin + (end - begin) / 2;
ReverseSort(array, begin, pivot + 1);
ReverseSort(array, pivot, end);
}
}
public static void ReverseSort(int[] array) {
ReverseSort(array, 0, array.Length);
}
P.S。:对不起我的英语。
答案 0 :(得分:3)
最好的情况是Theta(n),例如,对于排序的数组。最糟糕的情况是Theta(n ^ 2 log n)。
上限
辅助子问题的排序数组在任意元素之前或之后。这些是O(n log n)。如果在前面,我们做O(n)工作,在前半部分然后在后半部分解决次要子问题,然后做O(n)更多工作 - O(n log n)。如果成功,做O(n)工作,对已经排序的前半部分(O(n))进行排序,解决后半部分的次要子问题,做O(n)工作,解决上半部分的二级子问题,排序已排序后半部分(O(n)),做O(n)工作 - O(n log n)。
现在,在一般情况下,我们在两半上解决两个主子问题,然后使用辅助调用在枢轴上缓慢交换元素。必须进行O(n)交换,因此主定理的直接应用产生O(n ^ 2 log n)的界限。
下限
对于k&gt; = 3,我们使用上述分析作为指导,递归地构造大小为2 ^ k的阵列A(k)。不好的情况是数组[2 ^ k + 1] + A(k)。
设A(3)= [1,...,8]。这个有序的基本案例使Reverse
无法被调用。
对于k&gt; 3,设A(k)= [2 ^(k-1)+ A(k-1)[1],...,2 ^(k-1)+ A(k-1)[2 ^(k -1)]] + A(k-1)。注意,[2 ^ k + 1] + A(k)的主要子问题等于[2 ^(k-1)+ 1] + A(k-1)。
在主递归调用之后,数组为[2 ^(k-1)+ 1,...,2 ^ k,1,...,2 ^(k-1),2 ^ k + 1 ]。存在Omega(2 ^ k)元素必须移动Omega(2 ^ k)位置,并且到目前为止移动元素的每个辅助调用具有O(1)个排序的子问题,因此是Omega(n log n)。
显然需要更多咖啡 - 主要的子问题并不重要。这使得分析平均情况也不算太糟糕, Theta(n ^ 2 log n)。
具有恒定概率,数组的前半部分至少包含最小四分位数的一半,并且至少包含最大四分位数的一半。在这种情况下,无论Reverse
是否发生,都有Omega(n)元素必须通过二次调用移动Omega(n)位置。
答案 1 :(得分:1)
看起来这个算法,即使它对“随机”数据执行可怕(如Per在他们的回答中所示),对于“修复”“几乎排序”的数组非常有效。因此,如果你选择进一步发展这个想法(我个人不会,但如果你想把它当作一个练习),那么你应该把注意力放在这个力量上。
this reference on Wikipedia in the Inversion article很好地提到了这个问题。马哈茂德的书很有见地,并指出有各种方法可以衡量“排序”。例如,如果我们使用反转次数来表征“几乎排序的数组”,那么我们可以使用插入排序来非常快速地对它进行排序。但是,如果你的数组以稍微不同的方式“几乎排序”(例如一副切割或松散洗牌的卡片),那么插入排序将不是“修复”列表的最佳方式。
对于算法,我可能会这样做:
O(N lg(lg(N)))
,或者可以假设很小并跳过步骤)
有更好的方法可以做到这一点;如果你对数组进行足够的预处理或使用正确的数据结构,就像具有链表属性的数组或支持二进制搜索的类似数据一样,可以在至少O(log(N)*(# new elements))
时“修复”这样的数组。
你可以进一步概括这个想法。 “修复”阵列是否有效取决于所需的修复类型。因此,如果您在将元素添加到列表或修改它时更新这些统计信息,您可以调度到一个良好的“修复它”算法。
但不幸的是,这对代码来说都是一种痛苦。你可能只需要priority queue就可以逃脱。