高效遍历一个数组的所有排列组合情况

11 篇文章 0 订阅
3 篇文章 0 订阅

高效遍历一个数组的所有排列组合情况

1. 前言

本文主要是基于Aviad P.的2篇文章:A C# List Permutation IteratorA C# Combinations Iterator。分别介绍了如何遍历排列组合情况。使用的算法不需要额外分配空间,所以比较高效。

2. 实现

public static class Iterator
{
    private static void RotateLeft<T>(IList<T> sequence, int start, int count)
    {
        var tmp = sequence[start];
        sequence.RemoveAt(start);
        sequence.Insert(start + count - 1, tmp);
    }

    private static void RotateRight<T>(IList<T> sequence, int count)
    {
        var tmp = sequence[count - 1];
        sequence.RemoveAt(count - 1);
        sequence.Insert(0, tmp);
    }

    private static IEnumerable<IList<T>> Combinations<T>(IList<T> sequence, int start, int count, int choose)
    {
        if (choose == 0) {
            yield return sequence;
        }
        else {
            for (var i = 0; i < count; ++i) {
                foreach (var comb in Combinations(sequence, start + 1, count - 1 - i, choose - 1)) {
                    yield return comb;
                }
                RotateLeft(sequence, start, count);
            }
        }
    }

    /// <summary>
    /// 迭代从sequence中取出choose个元素的所有组合情况
    /// 即C(n,m),n为sequence.Count,m为choose
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <param name="sequence"></param>
    /// <param name="choose"></param>
    /// <returns>注意返回的list的长度没有缩短成choose,用户可以只遍历list的前choose个元素即可,或者调用Take(choose)取出前choose个元素</returns>
    public static IEnumerable<IList<T>> Combinations<T>(this IList<T> sequence, int choose)
    {
        return Combinations(sequence, 0, sequence.Count, choose);
    }

    private static IEnumerable<IList<T>> Permutations<T>(IList<T> sequence, int count)
    {
        if (count == 1) {
            yield return sequence;
        }
        else {
            for (var i = 0; i < count; ++i) {
                foreach (var perm in Permutations(sequence, count - 1)) {
                    yield return perm;
                }
                RotateRight(sequence, count);
            }
        }
    }

    /// <summary>
    /// 迭代sequence所有的排列情况
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <param name="sequence"></param>
    /// <returns></returns>
    public static IEnumerable<IList<T>> Permutations<T>(this IList<T> sequence)
    {
        return Permutations(sequence, sequence.Count);
    }

    /// <summary>
    /// 返回从字符串s中取出count个字母的所有组合情况
    /// </summary>
    /// <param name="s"></param>
    /// <param name="count"></param>
    /// <returns></returns>
    public static IEnumerable<string> Combinations(this string s, int count)
    {
        foreach (var comb in s.ToCharArray().ToList().Combinations(count)) {
            yield return string.Join(string.Empty, comb.Take(count));
        }
    }

    /// <summary>
    /// 返回字符串s的所有排列情况
    /// </summary>
    /// <param name="s"></param>
    /// <returns></returns>
    public static IEnumerable<string> Permutations(this string s)
    {
        foreach (var prem in s.ToCharArray().ToList().Permutations()) {
            yield return string.Join(string.Empty, prem);
        }
    }
}

3. 测试

foreach (var s in "abcdef".Combinations(3)) {
    Console.Write("{0,-8}", s);
}

Console.WriteLine("---------------------");

foreach (var s in "abc".Permutations()) {
    Console.Write("{0,-8}", s);
}

上面会输出

abc     abd     abe     abf     acd     ace     acf     ade     adf     aef     bcd     bce     bcf     bde     bdf     bef     cde     cdf     cef     def
---------------------
abc     bac     cab     acb     bca     cba

4. 总结

这个算法完全不需要分配额外空间,直接在原空间里进行遍历,所以对内存比较友好。但由于遍历过程会直接修改原数组,如果你不能接受这种情况,可以考虑在遍历前拷贝一份,对拷贝的那个数组进行遍历即可。

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
以下是一个简单的C语言程序,可以实现二维数组排列组合求和,并输出满足求和的所有元素下标: ```c #include <stdio.h> #define ROW 3 #define COL 4 void find_combinations(int arr[][COL], int target_sum); int main() { int arr[ROW][COL] = {{1, 2, 3, 4}, {5, 6, 7, 8}, {9, 10, 11, 12}}; int target_sum = 15; find_combinations(arr, target_sum); return 0; } void find_combinations(int arr[][COL], int target_sum) { int i, j, k, l, sum; for (i = 0; i < ROW; i++) { for (j = 0; j < COL; j++) { for (k = i; k < ROW; k++) { for (l = j; l < COL; l++) { sum = 0; int m, n; for (m = i; m <= k; m++) { for (n = j; n <= l; n++) { sum += arr[m][n]; } } if (sum == target_sum) { printf("Combination found: "); for (m = i; m <= k; m++) { for (n = j; n <= l; n++) { printf("(%d,%d) ", m, n); } } printf("\n"); } } } } } } ``` 这个程序中,我们首先定义了一个3x4的二维数组`arr`,并且给定了一个目标和`target_sum`为15。然后,我们调用了`find_combinations`函数,并将`arr`和`target_sum`作为参数传递进去。 在`find_combinations`函数中,我们使用四个嵌套的循环来遍所有的排列组合。对于每个排列组合,我们计算它们的和,并且与`target_sum`进行比较。如果它们相等,我们就输出所有满足求和的元素下标。 在这个程序中,我们使用了简单的暴力枚举算法来解决问题。在实际情况中,如果二维数组的规模很大,这个算法的效率可能会比较低。因此,我们可能需要使用更加高效的算法来解决这个问题。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值