对序列进行排序,但按另一个顺序排序某些特定元素

时间:2015-04-16 14:22:56

标签: algorithm sorting

假设我想按字母顺序对一系列名称进行排序,但还有一个额外的规则:

如果以下列表中有任何名称:

Mike Cathy James Albert Austin

出现后,它们将被移动到序列的 head ,并被排序为Mike -> Cathy -> James -> Albert -> Austin

例如,如果原始序列是这样的:

Conan,Cary,Clarence,Cathy,Mike,Blake,Baron,Vaughan,Albert,Gabriel,Cathy

期望的结果是:

Mike,Cathy,Cathy,Albert, Baron,Blake,Cary,Clarence,Conan,Gabriel,Vaughan

注意Mike, Cathy and Albert不再按字母顺序排序,它们作为一个整体先于其他常用名称,并具有自己的预定义顺序。


我的问题有一些进一步的解释:

  1. 原始序列以非平凡的方式检索(例如从数据库中),因此最好一次检索整个序列并在内存中对其进行排序。
  2. 无法保证原始序列中出现多少个特定名称,也不保证它们出现的次数。
  3. 有谁能告诉我如何以快速/有效的方式实现这一目标?

3 个答案:

答案 0 :(得分:4)

一种方法:

  1. 根据特殊排序存储桶中的成员资格,将名称列表(或过滤器,具体取决于您的语言)拆分为两个列表;
  2. 根据您的订单排序特殊名称列表;
  3. 按字典顺序排列第二个列表;
  4. 合并清单。
  5. 该方法应该适用于任何具有排序和列表或数组的语言来保存名称。

    在Python中:

    names=['Conan', 'Cary', 'Clarence', 'Cathy', 'Mike', 'Blake', 'Baron', 'Vaughan', 'Albert', 'Gabriel', 'Cathy']
    
    specials=['Mike', 'Cathy', 'James', 'Albert', 'Austin']
    # split the lists. 
    n1=[n for n in names if n in specials]  # ifilter would also work in Python
    n2=[n for n in names if n not in specials]
    
    # sort the first list based on order of specials, second lexicographically and combine:
    print sorted(n1, key=lambda n:specials.index(n))+sorted(n2)
    

    打印:

    ['Mike', 'Cathy', 'Cathy', 'Albert', 'Baron', 'Blake', 'Cary', 'Clarence', 'Conan', 'Gabriel', 'Vaughan']
    

    改进是:

    1. 创建一个双元素数据元素,包含作为第一个元素的特殊列表/数组的索引的否定整数,以及作为第二个元素的名称;
    2. 基于该元素的键或自定义cmp函数对列表/数组进行排序。
    3. 在Python中,您将在键功能中使用元组。在C中,您将编写自定义cmp函数。有了它,您可以根据语言的功能对names进行排序。

      元组中的两个元素将是特殊项中名称的否定索引(根据零基础索引进行调整),然后是名称。如果非零,则元组的第一个元素将胜过第二个元素。由于第一个元素是一个整数,即使specials中有超过10个名称,它也会正确排序。

      再次,在Python中:

      def cf(n):
          rtr=(specials.index(n)-len(specials)-1 if n in specials else 0, n)
          print rtr   # to show what is being generated for the sort key...
          return rtr
      
      names.sort(key=cf)   # sorts inplace
      

      打印:

      (0, 'Conan')
      (0, 'Cary')
      (0, 'Clarence')
      (-5, 'Cathy')
      (-6, 'Mike')
      (0, 'Blake')
      (0, 'Baron')
      (0, 'Vaughan')
      (-3, 'Albert')
      (0, 'Gabriel')
      (-5, 'Cathy')
      

      现在names已按原样排序并一次性传递到:

      ['Mike', 'Cathy', 'Cathy', 'Albert', 'Baron', 'Blake', 'Cary', 'Clarence', 'Conan', 'Gabriel', 'Vaughan']
      

答案 1 :(得分:1)

对于几乎任何软排序特殊情况,一个技巧是将元素转换为“按键”,按照您想要的方式排序,进行常规排序,然后将它们转换回来。

例如,

  1. 将“Gabriel”等常规名称改为“1.Gabriel”
  2. 将“Cathy”等特殊名称改为“0.1.Cathy”,将“Mike”改为“0.0.Mike”。
  3. 正常排序。前导零将在常规名称之前强制使用特殊名称。在特殊名称中,下一个数字将给出所需的排序顺序。在常规名称中,排序将是lexicographic。
  4. 撤消转换。由于原始转换只是添加了信息,因此您可以随时转换回来。
  5. 某些语言或库(如C或C ++)可以覆盖比较。这对于简单的案例来说是有用的(也是有效的)。其他语言(如Python)可以按需提供排序键,这使得这种方法非常简单。

    如果我记得,Knuth在 TAoCP 中有一个这种方法的例子。在那一个中​​,他有书名,排序规则就像是盲目的,将'A'和'the'移到标题的末尾等等。

答案 2 :(得分:0)

所以我的算法将是伪代码中的以下算法。

输入:字符串的向量V,字符串的侦听L(例外情况),与L

关联的顺序
Let pivot be a random element of V
Let A,B,C be three empty arrays of strings
For name in V
    if (name in L)
        add name to A 
    else if (name <= pivot)   // for the lexicographic order
        add name to B
    else if (name > pivot)    // for the lexicographic order
        add name to C
If (pivot in L)
    add pivot to A
Else
    add pivot to B
Sort A for the order associated with L
Sort B for the lexicographic order
Sort C for the lexicographic order
Merge B in A
Merge C in A
Return A