排序数字对的算法

时间:2011-03-16 10:25:44

标签: algorithm sorting

我遇到了一个问题,我需要一些来自SO的聪明人的帮助。 我有N对无符号整数。我需要对它们进行排序。对的结束向量应该按每对中的第一个数字非减少地排序,并且每对中的第二个数字不增加。每对可以具有彼此交换的第一和第二元素。有时候没有解决方案,所以我需要抛出异常。

示例:

in pairs:
1 5
7 1
3 8
5 6

out pairs:
1 7     <-- swapped
1 5     
6 5     <-- swapped
8 3     <-- swapped

^^没有交换对,就无法构建解决方案。所以我们交换对(7,1),(3,8)和(5,6)并构建结果。 或

in pairs:
1 5
6 9

out:
not possible

另一个显示“排序对”的示例不是解决方案。

in pairs:
1 4
2 5
out pairs:
1 4
5 2

由于

8 个答案:

答案 0 :(得分:15)

O(n log n)解决方案

enter image description here

答案 1 :(得分:2)

S(n)等于所有有效的排序顺序,其中n对应于包含[0,n]的对。

S(n) = []
for each order in S(n-1)
   for each combination of n-th pair
      if pair can be inserted in order, add the order after insertion to S(n)
      else don't include the order in S(n)

一对可以最多两种方式插入一个顺序(正常对和反向对)。

Maximum orderings = O(2^n)

我对这个摊销的订单不太确定,但是听我说。

对于订单和配对,我们有四种方法可以在插入后获取已排序的订单 (两个订单,一个(正常),一个(反向),零)

订单数量(摊销)=(1/4)* 2 +(1/4)* 1 +(1/4)* 1 +(1/4)* 0 = 1

 Amortized orderings = O(1)

同样,时间复杂度为O(n ^ 2),再次不确定。 以下程序使用Insertion sort的变体查找排序。

debug = False

(LEFT, RIGHT, ERROR) = range(3)
def position(first, second):
    """ Returns the position of first pair when compared to second """
    x,y = first
    a,b = second
    if x <= a and b <= y:
        return LEFT
    if x >= a and b >= y:
        return RIGHT
    else:
        return ERROR

def insert(pair, order):
    """ A pair can be inserted in normal order or reversed order
     For each order of insertion we will get one solution or none"""
    solutions = []
    paircombinations = [pair]
    if pair[0] != pair[1]: # reverse and normal order are distinct
        paircombinations.append(pair[::-1])

    for _pair in paircombinations:
        insertat = 0
        if debug: print "Inserting", _pair, 
        for i,p in enumerate(order):
            pos = position(_pair, p)
            if pos == LEFT:
                break
            elif pos == RIGHT:
                insertat += 1
            else:
                if debug: print "into", order,"is not possible"
                insertat = None
                break
        if insertat != None:
            if debug: print "at",insertat,"in", order
            solutions.append(order[0:insertat] + [_pair] + order[insertat:])
    return solutions


def swapsort(pairs):
    """
    Finds all the solutions of pairs such that ending vector
    of pairs are be sorted non decreasingly by the first number in
    each pair and non increasingly by the second in each pair.
    """
    solutions = [ pairs[0:1] ] # Solution first pair
    for pair in pairs[1:]:
        # Pair that needs to be inserted into solutions
        newsolutions = []
        for solution in solutions:
            sols = insert(pair, solution) # solutions after inserting pair
            if sols:
                newsolutions.extend(sols)
        if newsolutions:
            solutions = newsolutions
        else:
            return None
    return solutions

if __name__ == "__main__":
    groups = [ [(1,5), (7,1), (3,8), (5,6)],
               [(1,5), (2,3), (3,3), (3,4), (2,4)],
               [(3,5), (6,6), (7,4)],
               [(1,4), (2,5)] ]
    for pairs in groups:
        print "Solutions for",pairs,":"
        solutions = swapsort(pairs)
        if solutions:
            for sol in solutions:
                print sol
        else:
            print "not possible"

输出:

Solutions for [(1, 5), (7, 1), (3, 8), (5, 6)] :
[(1, 7), (1, 5), (6, 5), (8, 3)]
Solutions for [(1, 5), (2, 3), (3, 3), (3, 4), (2, 4)] :
[(1, 5), (2, 4), (2, 3), (3, 3), (4, 3)]
[(1, 5), (2, 3), (3, 3), (4, 3), (4, 2)]
[(1, 5), (2, 4), (3, 4), (3, 3), (3, 2)]
[(1, 5), (3, 4), (3, 3), (3, 2), (4, 2)]
Solutions for [(3, 5), (6, 6), (7, 4)] :
not possible
Solutions for [(1, 4), (2, 5)] :
[(1, 4), (5, 2)]

答案 2 :(得分:2)

这是一个有趣的问题。我独立提出了Tom的解决方案,这是我的Python代码:

class UnableToAddPair:
    pass

def rcmp(i,j):
    c = cmp(i[0],j[0])
    if c == 0:
        return -cmp(i[1],j[1])
    return c

def order(pairs):
    pairs = [list(x) for x in pairs]
    for x in pairs:
        x.sort()
    pairs.sort(rcmp)
    top, bottom = [], []
    for p in pairs:
        if len(top) == 0 or p[1] <= top[-1][1]:
            top += [p]
        elif len(bottom) == 0 or p[1] <= bottom[-1][1]:
            bottom += [p]
        else:
            raise UnableToAddPair
    bottom = [[x[1],x[0]] for x in bottom]
    bottom.reverse()
    print top + bottom

Tom的解决方案中没有提到的一个重点是,在排序阶段,如果任意两对中的较小值相同,则必须通过减小较大元素的值进行排序。

我花了很长时间才弄清楚为什么失败必须表明没有解决办法;我的原始代码已经回溯了。

答案 3 :(得分:1)

更新:由于问题已更改,此答案不再有效

通过第一个数字将对的矢量分割成桶。对每个桶进行降序排序。按照第一个数字的升序合并存储桶,并跟踪最后一对的第二个数量。如果它大于当前值,则没有解决方案。否则,您将在合并完成后获得解决方案。

如果你有稳定的排序算法,你可以按第二个数字降序排序,然后按第一个数字升序排序。之后检查第二个数字是否仍然是降序。

答案 4 :(得分:1)

下面是Python中一个简单的递归深度优先搜索算法:

import sys

def try_sort(seq, minx, maxy, partial):
  if len(seq) == 0: return partial
  for i, (x, y) in enumerate(seq):
    if x >= minx and y <= maxy:
      ret = try_sort(seq[:i] + seq[i+1:], x, y, partial + [(x, y)])
      if ret is not None: return ret
    if y >= minx and x <= maxy:
      ret = try_sort(seq[:i] + seq[i+1:], y, x, partial + [(y, x)])
      if ret is not None: return ret
  return None

def do_sort(seq):
  ret = try_sort(seq, -sys.maxint-1, sys.maxint, [])
  print ret if ret is not None else "not possible"

do_sort([(1,5), (7,1), (3,8), (5,6)])
do_sort([(1,5), (2,9)])
do_sort([(3,5), (6,6), (7,4)])

它维护一个已排序的子序列(partial),并尝试以原始顺序和相反的顺序将每个剩余的对附加到它,而不违反排序条件。

如果需要,可以轻松更改算法以查找所有有效的排序顺序。

编辑:我怀疑通过维护两个部分排序的序列(前缀和后缀)可以大大改善算法。我认为这样可以确定性地选择下一个元素,而不是尝试所有可能的元素。不幸的是,我现在没有时间考虑这个问题。

答案 5 :(得分:0)

在您的情况下交换只是一种2元素数组。 所以你可以 元组[] =(4,6),(1,5),(7,1),(8,6),......

    每个元组的
  1. - &gt;排序内部列表
  2. =&GT; (4,6),(1,5),(1,7),(6,8)

    1. 排序元组由1st asc
    2. =&GT; (1,5),(1,7),(4,6),(6,8)

      1. 排序元组由第1次desc
      2. =&GT; (1,7),(1,5),(4,6),(6,8)

答案 6 :(得分:0)

我注意到的第一件事是,如果一个元组中的两个值都大于任何其他元组中的两个值,则没有解决方案。

接下来我注意到,具有小差异的元组会向中间排序,并且具有较大差异的tupples会朝向末尾排序。

通过这两条信息,您应该能够找到合理的解决方案。

阶段1:对每个元组进行排序,首先移动较小的值。

第2阶段:对元组列表进行排序;首先按每个元组的两个值之间的差异的降序排列,然后按每个元组的第一个成员的升序对每个相等差异的分组进行排序。 (例如(1,6),(2,7),(3,8),(4,4),(5,5)。)

阶段3:检查异常。 1:查找一对元组,其中一个元组的两个元素都大于另一个元组的两个元素。 (例如,(4,4),(5,5)。)2:如果有四个或更多元组,则在每组元组中查看具有相同差异的三个或更多变量(例如(1,6) ,(2,7),(3,8)。)

阶段4:重新排列元组。从后端开始(具有最小差异的元组),具有相等差异的每个元组分组中的第二个变体必须交换它们的元素并且将元组附加到列表的后面。 (例如(1,6),(2,7),(5,5)=&gt;(2,7),(5,5),(6,1)。)

我认为这应该涵盖它。

答案 7 :(得分:0)

这是一个非常有趣的问题。这是我在VB.NET中的解决方案。

Module Module1

    Sub Main()
        Dim input = {Tuple.Create(1, 5),
                     Tuple.Create(2, 3),
                     Tuple.Create(3, 3),
                     Tuple.Create(3, 4),
                     Tuple.Create(2, 4)}.ToList

        Console.WriteLine(Solve(input))
        Console.ReadLine()
    End Sub

    Private Function Solve(ByVal input As List(Of Tuple(Of Integer, Integer))) As String
        Dim splitItems As New List(Of Tuple(Of Integer, Integer))
        Dim removedSplits As New List(Of Tuple(Of Integer, Integer))
        Dim output As New List(Of Tuple(Of Integer, Integer))
        Dim otherPair = Function(indexToFind As Integer, startPos As Integer) splitItems.FindIndex(startPos, Function(x) x.Item2 = indexToFind)
        Dim otherPairBackwards = Function(indexToFind As Integer, endPos As Integer) splitItems.FindLastIndex(endPos, Function(x) x.Item2 = indexToFind)

        'split the input while preserving their indices in the Item2 property
        For i = 0 To input.Count - 1
            splitItems.Add(Tuple.Create(input(i).Item1, i))
            splitItems.Add(Tuple.Create(input(i).Item2, i))
        Next

        'then sort the split input ascending order
        splitItems.Sort(Function(x, y) x.Item1.CompareTo(y.Item1))

        'find the distinct values in the input (which is pre-sorted)
        Dim distincts = splitItems.Select(Function(x) x.Item1).Distinct

        Dim dIndex = 0
        Dim lastX = -1, lastY = -1

        'go through the distinct values one by one
        Do While dIndex < distincts.Count
            Dim d = distincts(dIndex)

            'temporary list to store the output for the current distinct number
            Dim temOutput As New List(Of Tuple(Of Integer, Integer))

            'go through each of the split items and look for the current distinct number
            Dim curIndex = 0, endIndex = splitItems.Count - 1
            Do While curIndex <= endIndex
                If splitItems(curIndex).Item1 = d Then
                    'find the pair of the item
                    Dim pairIndex = otherPair(splitItems(curIndex).Item2, curIndex + 1)
                    If pairIndex = -1 Then pairIndex = otherPairBackwards(splitItems(curIndex).Item2, curIndex - 1)

                    'create a pair and add it to the temporary output list
                    temOutput.Add(Tuple.Create(splitItems(curIndex).Item1, splitItems(pairIndex).Item1))

                    'push the items onto the temporary storage and remove it from the split list
                    removedSplits.Add(splitItems(curIndex))
                    removedSplits.Add(splitItems(pairIndex))
                    If curIndex > pairIndex Then
                        splitItems.RemoveAt(curIndex)
                        splitItems.RemoveAt(pairIndex)
                    Else
                        splitItems.RemoveAt(pairIndex)
                        splitItems.RemoveAt(curIndex)
                    End If
                    endIndex -= 2
                Else
                    'increment the index or exit the iteration as appropriate
                    If splitItems(curIndex).Item1 <= d Then curIndex += 1 Else Exit Do
                End If
            Loop

            'sort temporary output by the second item and add to the main output
            output.AddRange(From r In temOutput Order By r.Item2 Descending)

            'ensure that the entire list is properly ordered
            'start at the first item that was added from the temporary output
            For i = output.Count - temOutput.Count To output.Count - 1
                Dim r = output(i)
                If lastX = -1 Then
                    lastX = r.Item1
                ElseIf lastX > r.Item1 Then
                    '!+ It appears this section of the if statement is unnecessary
                    'sorting on the first column is out of order so remove the temporary list
                    'and send the items in the temporary list back to the split items list
                    output.RemoveRange(output.Count - temOutput.Count, temOutput.Count)
                    splitItems.AddRange(removedSplits)
                    splitItems.Sort(Function(x, y) x.Item1.CompareTo(y.Item1))
                    dIndex += 1
                    Exit For
                End If
                If lastY = -1 Then
                    lastY = r.Item2
                ElseIf lastY < r.Item2 Then
                    'sorting on the second column is out of order so remove the temporary list
                    'and send the items in the temporary list back to the split items list
                    output.RemoveRange(output.Count - temOutput.Count, temOutput.Count)
                    splitItems.AddRange(removedSplits)
                    splitItems.Sort(Function(x, y) x.Item1.CompareTo(y.Item1))
                    dIndex += 1
                    Exit For
                End If
            Next
            removedSplits.Clear()
        Loop

        If splitItems.Count = 0 Then
            Dim result As New Text.StringBuilder()
            For Each r In output
                result.AppendLine(r.Item1 & " " & r.Item2)
            Next

            Return result.ToString

        Else
            Return "Not Possible"
        End If
    End Function

    <DebuggerStepThrough()> _
    Public Class Tuple(Of T1, T2)
        Implements IEqualityComparer(Of Tuple(Of T1, T2))

        Public Property Item1() As T1
            Get
                Return _first
            End Get
            Private Set(ByVal value As T1)
                _first = value
            End Set
        End Property
        Private _first As T1

        Public Property Item2() As T2
            Get
                Return _second
            End Get
            Private Set(ByVal value As T2)
                _second = value
            End Set
        End Property
        Private _second As T2

        Public Sub New(ByVal item1 As T1, ByVal item2 As T2)
            _first = item1
            _second = item2
        End Sub

        Public Overloads Function Equals(ByVal x As Tuple(Of T1, T2), ByVal y As Tuple(Of T1, T2)) As Boolean Implements IEqualityComparer(Of Tuple(Of T1, T2)).Equals
            Return EqualityComparer(Of T1).[Default].Equals(x.Item1, y.Item1) AndAlso EqualityComparer(Of T2).[Default].Equals(x.Item2, y.Item2)
        End Function

        Public Overrides Function Equals(ByVal obj As Object) As Boolean
            Return TypeOf obj Is Tuple(Of T1, T2) AndAlso Equals(Me, DirectCast(obj, Tuple(Of T1, T2)))
        End Function

        Public Overloads Function GetHashCode(ByVal obj As Tuple(Of T1, T2)) As Integer Implements IEqualityComparer(Of Tuple(Of T1, T2)).GetHashCode
            Return EqualityComparer(Of T1).[Default].GetHashCode(Item1) Xor EqualityComparer(Of T2).[Default].GetHashCode(Item2)
        End Function
    End Class

    Public MustInherit Class Tuple
        <DebuggerStepThrough()> _
        Public Shared Function Create(Of T1, T2)(ByVal first As T1, ByVal second As T2) As Tuple(Of T1, T2)
            Return New Tuple(Of T1, T2)(first, second)
        End Function
    End Class

End Module

输入

1 5
2 3
3 3
3 4
2 4

生成输出

1 5
2 4
2 3
3 4
3 3

3 5
6 6
7 4

输出

Not Nossible

评论

我发现这个问题非常具有挑战性。我花了大约15分钟来提出一个解决方案,花了一个小时左右来编写和调试它。代码中充斥着评论,以便任何人都可以关注它。