JavaScript實現元素全排列


文章出自個人博客 https://knightyun.github.io/2019/05/11/js-permutation,轉載請申明。


排列 (Permutation / Arrangement)

概念

n 個不同元素中任意選取 m (m <= n) 個元素進行排列,所有排列情況的個數叫做 排列數,其值等於:

A = n! / (n - m)!

! 表示數學中的階乘運算符,可以通過以下函數實現:

function factorial(n) {
    if (n === 0 || n === 1) {
        return 1;
        
    } else if (n < 0) {
        return null;
        
    } else {
        return n * factorial(n - 1);
    }
}

console.log(factorial(4)); // 24

當 n = m 時,稱爲 全排列,其值等於:

A = n!

全排列相當於將所有元素進行排序,得到所有不同順序情況的個數;

分析

利用階乘函數,通過上述數學公式只能得到所有情況的個數值,不容易得到具體的每種情況,要獲取每種情況的輸出值的話需要另尋他法;

用數組舉例分析:

全排列:

    [1, 2, 3] => [              
                    [1, 2, 3], 
                    [1, 3, 2], 
                    [2, 1, 3], 
                    [2, 3, 1], 
                    [3, 1, 2], 
                    [3, 2, 1]
                 ]
                
                共 6 種情況

    樹狀圖表示:
    
      1       2       3
     / \     / \     / \
    2   3   1   3   1   2
    |   |   |   |   |   |
    3   2   3   1   2   1   =>  6 

3 個元素中選取 2 個時:(n = 3, m = 2)

    [1, 2, 3] => [              
                    [1, 2], 
                    [1, 3], 
                    [2, 1], 
                    [2, 3], 
                    [3, 1], 
                    [3, 2]
                 ]
                
                共 6 種情況
    
    樹狀圖表示:
    
      1       2       3
     / \     / \     / \
    2   3   1   3   1   2   =>  6

實現

let arr = [1, 2, 3];

/*
參數 a 爲輸入數組,
元素個數 n 爲 a 的長度,
選取個數爲 m;
*/
function permutation(a, m) {

    // 保存最終輸出結果
    let result = [];
    
    // 定義 m 值默認等於 n,即全排列
    let n = a.length;
    m = m || n;
    
    // 定義遞歸函數保存結果到數組中
    // _a 爲輸入數組,
    // tmpResult 爲保存單個情況結果的數組
    function recur(_a, tmpResult = []) {
        if (tmpResult.length === m) {
        
            // 結果達到 m 個時保存結果,
            // 停止遞歸併進入下一次遍歷
            result.push(tmpResult);
            
        } else {
            for (let i = 0; i < _a.length; i++) {
                
                // 複製一份輸入數組,防止引用值被改變
                let tmpA = _a.concat();
            
                // 複製一份保存結果的數組,防止每次遍歷相互影響
                let _tmpResult = tmpResult.concat();
                
                // 保存當前遍歷值
                _tmpResult.push(tmpA[i]);
                
                // 刪除當前遍歷值,傳遞參數進入下一層遞歸
                tmpA.splice(i, 1);
                recur(tmpA, _tmpResult);
            }
        }
    }
    
    // 開始執行遞歸,然後返回最後結果
    recur(a);
    return result;
}

console.log(permutation(arr));
// 3 個數全排列:
/*
[              
    [1, 2, 3], 
    [1, 3, 2], 
    [2, 1, 3], 
    [2, 3, 1], 
    [3, 1, 2], 
    [3, 2, 1]
]
*/

console.log(permutation(arr, 2));
// 3 個數中選取 2 個數排列:
/*
[              
    [1, 2], 
    [1, 3], 
    [2, 1], 
    [2, 3], 
    [3, 1], 
    [3, 2]
]
*/

最終實現函數就是 permutation(a, m),其中參數 a 爲輸入數組,包含需要排列的所有元素,參數 m 爲選取需要排列的個數,默認等於輸入數組的長度,即默認全排列,注意 m 不能大於元素個數;

拓展

以上函數輸出值爲一個二維數組,如果需要便於觀察,輸出一個一維數組,可以定義一個合併函數:

function merge(arr) {
    return arr.map(x => x.join(''));
}

let result = merge(permutation([1, 2, 3]));
console.log(result);
// [123, 132, 213, 231, 312, 321]

技術文章推送
手機、電腦實用軟件分享
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章