在没有 for 循环的情况下查找数组的排列

问题描述 投票:0回答:7

我在 LinkedIn 群组上看到了这个面试问题这里

总而言之,如果我有一个数组

[1,2,3,4,5]

并输入

3

我需要输出

[1,2,3], [3,2,1], [2,3,1], [2,1,3], [1,3,2], [3,1,2], [2,3,4], [4,3,2],...

排名不分先后。

我已经思考这个问题有一段时间了。我想出了各种不同的解决方法,但所有方法都使用 for 循环。

我认为很明显,为了消除循环,它必须是递归的。

我以为我已经接近递归地分区数组和连接元素了,但是非常沮丧,我最终需要另一个 for 循环。

我开始认为这是不可能的(这是不可能的,否则为什么要问面试问题?)。

有什么想法或链接吗?可能的输出数量应为 5PN,其中 N 是输入。

arrays algorithm permutation
7个回答
1
投票

以下递归算法将尝试打印 {1,.., n} 的每个子集。这些子集通过以下双射与 0 到 2^n-1 之间的数字一对一:对于 0 到 2^n-1 之间的整数 x,如果 x 的第一位设置为,则关联包含 1 的集合1, 2 如果 x 的第二位设置为 1, ..

void print_all_subsets (int n, int m, int x) {
    if (x==pow(2,n)) {
        return;
    }
    else if (x has m bits set to one) {
        print the set corresponding to x;
    }
    print_all_subsets(n,m,x+1);
}

您需要使用 n = 5(根据您的情况)、m=3(根据您的情况)和 x = 0 来调用它。

然后你需要实现两个函数“打印对应于x的集合”和“x有m个位设置为1”而不需要for循环......但这很容易使用递归来完成。

然而,我认为这更像是一个挑战——完全消除 for 循环是没有意义的,有意义的只是以聪明的方式使用它们。


1
投票

你的第一个想法是对的。每个循环都可以用递归代替。在某些语言中(例如Scheme),循环实际上是通过递归实现的。因此,从任何解决方案开始,然后继续将循环变成递归。最终你会完成的。

这是 Python 中的一个可行解决方案。

def subsets_of_size (array, size, start=0, prepend=None):
    if prepend is None:
        prepend = [] # Standard Python precaution with modifiable defaults.
    if 0 == size:
        return [[] + prepend] # Array with one thing.  The + forces a copy.
    elif len(array) < start + size:
        return [] # Array with no things.
    else:
        answer = subsets_of_size(array, size, start=start + 1, prepend=prepend)
        prepend.append(array[start])
        answer = answer + subsets_of_size(array, size-1, start=start + 1, prepend=prepend)
        prepend.pop()
        return answer

print subsets_of_size([1,2,3,4,5], 3)

0
投票

我认为解决方案不是不使用 for 循环,而是有一种使用 for 循环的最佳方法。

因此,就有了堆算法。以下来自 wiki http://en.wikipedia.org/wiki/Heap%27s_algorithm

procedure generate(n : integer, A : array of any):
    if n = 1 then
          output(A)
    else
        for i := 0; i < n; i += 1 do
            generate(n - 1, A)
            if n is even then
                swap(A[i], A[n - 1])
            else
                swap(A[0], A[n-1])
            end if
        end for
    end if

0
投票
define listPermutations:
    input: int p_l , int[] prevP , int atElement , int[] val , int nextElement
    output: list

    if nextElement > length(val) OR atElement == p_l OR contains(prevP , val[nextElement]
        return EMPTY

    list result

    int[] tmp = copy(prevP)
    tmp[atElement] = val[nextElement]
    add(result , tmp)

    //create the next permutation stub with the last sign different to this sign 
    //(node with the same parent)
    addAll(result , listPermutations(p_l , tmp , atElement , val , nextElement + 1))

    //create the next permutation stub with an additional sign
    //(child node of the current permutation
    addAll(result , listPermutations(p_l , tmp , atElement + 1 , val , 0))

    return result

//this will return the permutations for your example input:
listPermutations(3 , new int[3] , 0 , int[]{1 , 2 , 3 , 4 , 5} , 0)

基本思想:给定数量元素的所有排列形成一棵树,其中节点是空排列,并且节点的所有子节点都有一个附加元素。现在算法要做的就是逐层遍历这棵树,直到该层等于排列所需的长度并列出该层上的所有节点


0
投票

您可以在这里使用递归,每次调用内部级别时,您都给它它在数组中的位置,当它返回时,它返回一个增加的位置。为此,您将使用一个

while
循环。

伪代码:

int[] input = [1,2,3,4,5];
int level = 3;

int PrintArrayPermutation(int level, int location, string base)
{
    if (level == 0)
    {
        print base + input[location];
        return location + 1;
    }

    while (location <= input.Length)
    {
        location = 
            PrintArrayPermutation(level - 1, location, base + input[location]);
    }
}

这是我想法的一个非常基本的轮廓。


0
投票

这是 JavaScript 中的两个递归函数。第一个是组合

choose
函数,我们应用第二个函数,排列每个结果(
permutator
改编自 SO 用户,分隔的,在这里回答:JavaScript 中的排列?

function c(n,list){
  var result = [];
  function _c(p,r){
    if (p > list.length)
      return
    if (r.length == n){
      result = result.concat(permutator(r));
    } else {
      var next = list[p],
          _r = r.slice();
      _r.push(next)
      _c(p+1,_r);
      _c(p+1,r);
    }
  }
  _c(0,[])
  return result;
}

function permutator(inputArr) {
  var results = [];

  function permute(arr, memo) {
    var cur, memo = memo || [];

    function _permute (i,arr,l){
      if (i == l)
        return
      cur = arr.splice(i,1);
      if (arr.length === 0){
        results.push(memo.concat(cur));
      }
      permute(arr.slice(), memo.concat(cur));
      arr.splice(i, 0, cur[0]);
      _permute(i + 1,arr,l)
    }
    _permute(0,arr,arr.length);
    return results;
  }

  return permute(inputArr);
}

输出:

console.log(c(3,[1,2,3,4,5]))

[[1,2,3],[1,3,2],[2,1,3]...[4,5,3],[5,3,4],[5,4,3]]

0
投票

我们可以使用递归以及使用队列来完成。

注意:它使用循环而不是多个循环

使用递归:

function permutations(items, size, withReplacement=false, results = [[]]) {
    if(size === 0) {
        return results;
    }

    const newResults = [];
    results.forEach((arr) => {
        items.forEach((item) => {
            if(withReplacement || !arr.includes(item)) {
                newResults.push([...arr, item])
            }
        })
    })
    
    console.log(newResults);
    return permutations(items, size - 1, withReplacement, newResults)
}

使用队列:

function permutationsWithQue(items, size, withReplacement=false) {
    const queue = [[]];
    const results = [];
    while(queue.length) {
        const curPerm = queue.shift()
        for(const item of items) {
            if(!withReplacement && curPerm.includes(item)) {
                continue;
            }
            const newPerm = [...curPerm, item];
            if(newPerm.length === size) {
                results.push(newPerm);
            }
            else {
                queue.push(newPerm)
            }
        }
    }
    console.log(results);

    return results;
}

调用函数如

permutations([1, 2, 3, 4,5], 3, false);
permutationsWithQue([1, 2, 3, 4,5], 3, false);
© www.soinside.com 2019 - 2024. All rights reserved.