用于基于另一个部分列表的排序来排序列表的算法

时间:2015-08-05 15:09:56

标签: algorithm list sorting

此问题与基于另一个列表的顺序排序列表主题的其他问题不同,因为订单列表不包含列表中使用的所有密钥。

说我有一个列表[a, b, c, d, e]和我的订单列表[b, d, e]

现在我将订单列表更改为[b, e, d]。是否有一个相对简单的算法来采用原始列表?假设最终排序是[a, b, e, c, d]还是[a, b, c, e, d]并不重要,订单列表将始终是原始列表的子集。

修改:从我的示例中清除有关最终排序的一些问题:e被命令介于bd之间,并且在排序列表中无关紧要如果e最终与bd相邻。但是,例如,如果由于此排序a移至b之后 - 而合法排序 - 则不可取。

5 个答案:

答案 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]