此问题与基于另一个列表的顺序排序列表主题的其他问题不同,因为订单列表不包含列表中使用的所有密钥。
说我有一个列表[a, b, c, d, e]
和我的订单列表[b, d, e]
。
现在我将订单列表更改为[b, e, d]
。是否有一个相对简单的算法来采用原始列表?假设最终排序是[a, b, e, c, d]
还是[a, b, c, e, d]
并不重要,订单列表将始终是原始列表的子集。
修改:从我的示例中清除有关最终排序的一些问题:e
被命令介于b
和d
之间,并且在排序列表中无关紧要如果e
最终与b
或d
相邻。但是,例如,如果由于此排序a
移至b
之后 - 而合法排序 - 则不可取。
答案 0 :(得分:2)
您可以通过设置Dennis Callanan建议的自定义比较器来完成您想要的任务,然后对阵列执行稳定排序。 Quicksort不是一个稳定的类型;它通常会改变未包含在部分排序中的元素的顺序。合并排序是一种稳定的排序。
如果在比较器中使用线性搜索,算法将在时间O(n ^ 2 log n)运行。要使其运行得更快,您需要做的是让一个人运行新数组,散列数组中每个元素的位置以便快速查找。
我可以用Java实现它,但我不懂Python,抱歉。
另一个观点是使用拓扑排序。在原始列表中,您有一个 - > b - > c - > d - > e箭头表示“来之前”b。然后添加新数据b - > e - > d。你必须打破第一个列表中任何导致矛盾的箭头,即d - >即你拥有的是一堆箭头:
a - > b,b - > c,c - > d,b - > e,e - > d
如果这是一个有向无环图(即没有矛盾),那么你可以在O(V + E)时间toposort。由于边数最多为2n,这是O(n)时间,非常有效。
问题在于决定原始列表中哪些箭头可以突破(也可能用其他箭头替换),这样就不存在任何矛盾。一般来说,这是一个名为minimum feedback arc set的NP难题,但我怀疑你的问题结构中存在一些会使其运行得更快的问题。
最后,用新阵列给出的置换替换(非连续)子阵列中的元素[...,b,...,d,e]怎么样?这可以在O(n)时间内完成。
答案 1 :(得分:0)
一种天真的n ^ 2方法是:
for each S in order-list
if S is in other-list
remove S from other-list
add S to end of other-list
这将导致您的排序列表中的项目被删除并附加到您的其他列表中:[a, c, b, e, d]
答案 2 :(得分:0)
Python方法,可以用任何语言轻松实现(Java不需要functools
)
import functools
order = [5, 1, 4]
def numeric_compare(x, y):
if x in order and y in order:
if order.index(x) > order.index(y):
return 1
elif order.index(x) < order.index(y):
return -1
else:
if x in order:
return -1
elif y in order:
return 1
return 0
a = [1,2,3,4,5]
a.sort(key = functools.cmp_to_key(numeric_compare))
答案 3 :(得分:0)
编辑这是一个稍微高效的版本;而不是在每次迭代中查找订单元素的索引(arr.indexOf),只需在开头查看一次,保持索引更新。
最差时间,如果N是数组的长度,M是你的订单长度是O(N * M + M ^ 2)
function partialSort(arr, order) {
var orderIndex = [];
for(var i = 0; i < order.length; i++) {
orderIndex[i] = arr.indexOf(order[i]);
}
for(var i = 0; i < orderIndex.length; i++) {
var indexI = orderIndex[i];
for(var j = i + 1; j < orderIndex.length; j++) {
var indexJ = orderIndex[j];
if(indexI > indexJ) {
var temp = arr[indexI];
arr[indexI] = arr[indexJ];
arr[indexJ] = temp;
orderIndex[i] = indexJ;
orderIndex[j] = indexI;
indexI = indexJ;
}
}
}
return arr;
}
var a = [1, 2, 3, 4, 5, 6, 7];
var o = [3,5,7];
console.log(o + "\n" + partialSort(a, o));
o = [5,3,7];
console.log(o + "\n" + partialSort(a, o));
o = [7,3,5];
console.log(o + "\n" + partialSort(a, o));
这里是使用MlogM排序O的版本(N * M + M * log(M)+ M)
function partialSort(arr, order) {
var orderIndex = [];
for(var i = 0; i < order.length; i++) {
orderIndex[i] = arr.indexOf(order[i]);
}
// sort by index ~some quick sort variant O(M * log(M))
orderIndex.sort(function(a, b) { return a - b; });
// put the ordered elements in the correct sequence in the main array
for(var i = 0; i < orderIndex.length; i++) {
arr[orderIndex[i]] = order[i];
}
return arr;
}
如果您想要绝对最佳效率,可以用基数排序O(N * M + M + M)替换 orderIndex.sort
答案 4 :(得分:0)
我为另一个问题(Javascript Double sorting algorithm)编写了一个quicksort版本,我不知道它是否完全可靠。它最初只是为了排序字符串或只是数字,但似乎适应这种情况(我改变了“isNumber”和“less”函数):
function isNumber(x,y) {
return (map[x]);
}
function less(a,b,y){
return y ? a < b : map[a] < map[b];
}
function swap(a, i, j) { var t = a[i]; a[i] = a[j]; a[j] = t; }
function partition(array, pivot, left, right, what) {
var store = left,
pivotValue = array[pivot];
swap(array, pivot, right);
for (var v = left; v < right; v++) {
if (less(array[v],pivotValue,what) && isNumber(array[v],what)) {
swap(array, v, store);
store++;
}
}
while(!isNumber(array[store],what))
store++;
swap(array, right, store);
return store;
}
function doubleQSort(array, left, right, what) {
while(!isNumber(array[right],what) && right > left)
right--;
while(!isNumber(array[left],what) && left < right)
left++;
var pivot = null;
if (left < right) {
pivot = (right + left) >> 1;
while(!isNumber(array[pivot],what))
pivot--;
newPivot = partition(array, pivot, left, right, what);
doubleQSort(array, left, newPivot - 1,what);
doubleQSort(array, newPivot + 1, right,what);
}
}
输出:
var things = ['a', 'b', 'c', 'd', 'e'];
var map = {'b':1, 'e':2, 'd':3}
doubleQSort(things,0,things.length - 1);
console.log(things) // [a, b, c, e, d]