按字典顺序排序的堆栈排列

Stack permutations sorted in lexicographical order

数字 N 的堆栈排列定义为您可以通过执行以下操作打印的序列数

  1. 保留两个堆栈,比如 A 和 B。
  2. 将B中的数字从1倒序推入N。(所以B的顶部是1,B中的最后一个元素是N)
  3. 进行以下操作

按某种顺序进行这些操作得到的所有可能序列称为stack permutations

例如:N = 2
堆栈排列是 (1, 2) 和 (2, 1)
例如:N = 3
堆栈排列是 (1, 2, 3), (1, 3, 2), (2, 1, 3), (2, 3, 1) 和 (3, 2, 1)

N个数的堆栈排列数为C(N),其中C(N)为第N个Catalan Number

假设我们生成给定N的所有堆栈排列,然后按字典顺序(字典顺序)打印它们,我们如何确定第k个排列,而不是实际生成所有排列然后对它们进行排序?

我想要一些可编程的算法方法。

你没有说 k 应该基于 0 还是基于 1。我选择了0。切换回来很容易。

方法是先写一个函数来计算从给定的决策点开始有多少堆栈排列。使用记忆使其快速。然后通过跳过导致字典序更小的排列的决策,沿着决策树继续前进。这将导致您想要的决定列表。

def count_stack_permutations (on_b, on_a=0, can_take_from_a=True, cache={}):
    key = (on_b, on_a, can_take_from_a)
    if on_a < 0:
        return 0 # can't go negative.
    elif on_b == 0:
        if can_take_from_a:
            return 1 # Just drain a
        else:
            return 0 # Got nothing.
    elif key not in cache:
        # Drain b
        answer = count_stack_permutations(on_b-1, on_a, True)
        # Drain a?
        if can_take_from_a:
            answer = answer + count_stack_permutations(on_b, on_a-1, True)
        # Move from b to a.
        answer = answer + count_stack_permutations(on_b-1, on_a+1, False)
        cache[key] = answer

    return cache[key]

def find_kth_permutation (n, k):
    # The end of the array is the top
    a = []
    b = list(range(n, 0, -1))
    can_take_from_a = True # We obviously won't first. :-)

    answer = []
    while 0 < max(len(a), len(b)):
        action = None
        on_a = len(a)
        on_b = len(b)

        # If I can take from a, that is always smallest.
        if can_take_from_a:
            if count_stack_permutations(on_b, on_a - 1, True) <= k:
                k = k - count_stack_permutations(on_b, on_a - 1, True)
            else:
                action = 'a'
        # Taking from b is smaller than digging into b so I can take deeper.
        if action is None:
            if count_stack_permutations(on_b-1, on_a, True) <= k:
                k = k - count_stack_permutations(on_b-1, on_a, True)
            else:
                action = 'b'
        # Otherwise I will move.
        if action is None:
            if count_stack_permutations(on_b-1, on_a, False) < k:
                return None # Should never happen
            else:
                action = 'm'

        if action == 'a':
            answer.append(a.pop())
            can_take_from_a = True
        elif action == 'b':
            answer.append(b.pop())
            can_take_from_a = True
        else:
            a.append(b.pop())
            can_take_from_a = False
    return answer

# And demonstrate it in action.
for k in range(0, 6):
    print((k, find_kth_permutation(3, k)))

这可以使用 factoradic(https://en.wikipedia.org/wiki/Factorial_number_system)

如果您需要 Java 中的快速解决方案,请使用 JNumberTools

JNumberTools.permutationsOf("A","B","C")
                .uniqueNth(4) //next 4th permutation
                .forEach(System.out::println);

这个API会直接按照字典顺序生成下一个第n个排列。因此,您甚至可以生成 100 个项目的下一个十亿次排列。

要生成给定大小的下一个第 n 个排列,请使用:

JNumberTools.permutationsOf("A","B","C")
                .kNth(2,4) //next 4th permutation of size 2
                .forEach(System.out::println);

JNumberTools 的 Maven 依赖项是:

<dependency>
    <groupId>io.github.deepeshpatel</groupId>
    <artifactId>jnumbertools</artifactId>
    <version>1.0.0</version>
</dependency>