【算法】排列組合之M選N(多是最簡的代碼)

首先,M選N的算法規則:要從長度爲M的數組中選N個數,列出全部組合java

  1. 生成一個長度爲M的新數組;
  2. 初始化新數組,前N位填充爲1,其他填充爲0;
  3. 從左往右,找到第一個【10】,則交換1和0,即替換爲0和1;
  4. 將替換後的1的左邊的全部1移動到最左端;
  5. 重複步驟3;
  6. 若是沒有找到【10】,則結束;

例子:求5中選3的組合,用數學公式計算組合爲C5-3=(5x4x3)/(3x2x1)=10,算法窮舉法驗證:算法

1 2 3 4 5(原始數組)
1 1 1 0 0 -->1,2,3
1 1 0 1 0 -->1,2,4
1 0 1 1 0 -->1,3,4
0 1 1 1 0 -->2,3,4
1 1 0 0 1 -->1,2,5
1 0 1 0 1 -->1,3,5
0 1 1 0 1 -->2,3,5
1 0 0 1 1 -->1,4,5
0 1 0 1 1 -->2,4,5
0 0 1 1 1 -->3,4,5數組

下面用代碼實現:優化

/**
     * M選N算法
     */
    public static List m2n(List source, int n) {
        List list = Lists.newArrayList();
        
        // 初始化新數組,長度和原數組一致,而且前n爲填充1,其他爲0
        int size = source.size();
        Integer[] zero = new Integer[size];
        for (int i = 0; i < size; i++) {
            if (i < n) {
                zero[i] = 1;
            } else {
                zero[i] = 0;
            }
        }

        // 尋找【10】,交換,移動
        for (; ; ) {
            
            // 新數組的1的下標對應原數組的下標,生成結果集
            List sublist = Lists.newArrayList();
            for (int i = 0; i < size; i++) {
                if (zero[i].equals(1)) {
                    sublist.add(source.get(i));
                }
            }
            list.add(sublist);

            // 尋找【10】組合,交換爲【01】,若是沒找到,則表明已結束
            int i = Joiner.on("").join(zero).indexOf("10");
            if (i == -1) {
                break;
            }
            zero[i] = 0;
            zero[i + 1] = 1;

            // 統計交換後的01位置以前有多少個1,並將其移動到最左端
            long count = Arrays.asList(zero).subList(0, i).stream().filter(o -> o.equals(1)).count();
            for (int j = 0; j < i; j++) {
                if (j < count) {
                    zero[j] = 1;
                } else {
                    zero[j] = 0;
                }
            }
        }
        return list;
    }

歡迎留言指出能夠優化更簡的地方,謝謝。code

相關文章
相關標籤/搜索