Permutations 全排列生成算法

1、使用遞歸生成算法

/*
 * 遞歸輸出序列的全排列
 */
void permutation(char* array, size_t array_size, unsigned int index)
{
	if(index >= array_size)
	{
		for(unsigned int i = 0; i < array_size; ++i)
		{
			cout << array[i] << ' ';
		}

		cout << '\n';

		return;
	}

	for(unsigned int i = index; i < array_size; ++i)
	{
		swap(array, i, index);

		<span style="font-family: Arial, Helvetica, sans-serif;">permutation</span>(array, array_size, index + 1);

		swap(array, i, index); //相當於回溯
	}
}
遞歸算法總是那麼容易的理解,一般的問題是沒有問題的,但是有個缺點,就是當序列中有重複的元素,上面的遞歸需要修改才能得到全排列

2、使用字典序算法

所謂字典序算法,就是根據當前字典序查找下一個m大於當前字典序並且不存在一個大於當前序列小於m的序列,即大於當前序列的最小值序列

字典序排序生成算法

字典序法就是按照字典排序的思想逐一產生所有排列。

例如,由1,2,3,4組成的所有排列,從小到大的依次爲:

1234, 1243, 1324, 1342, 1423, 1432, 
2134, 2143, 2314, 2341, 2413, 2431, 
3124, 3142, 3214, 3241, 3412, 3421, 
4123, 4132, 4213, 4231, 4312, 4321.

分析這種過程,看後一個排列與前一個排列之間有什麼關係?

再如,設有排列(p)=2763541,按照字典式排序,它的下一個排列是什麼?

  1. 2763541 (找最後一個正序35)
  2. 2763541 (找3後面比3大的最後一個數4)
  3. 2764531 (交換3,4的位置)
  4. 2764135 (把4後面的5,3,1反轉)

下面給出求 p[1…n] 的下一個排列的描述:

  1. 求 i = max{j | p[j – 1] < p[j]} (找最後一個正序)
  2. 求 j = max{k| p[i – 1] < p[k]} (找最後大於 p[i – 1] 的)
  3. 交換 p[i – 1] 與 p[j]得到 p[1] … p[i-2] p[j] p[i] p[i+1] … p[j-1] p[i-1] p[j+1] … p[n]
  4. 反轉 p[j] 後面的數得到 p[1] … p[i-2] p[j] p[n] … p[j+1] p[i-1] p[j-1] … p[i]

Next Permutation

Description:

  • Implement next permutation, which rearranges numbers into the lexicographically next greater permutation of numbers.
  • If such arrangement is not possible, it must rearrange it as the lowest possible order (ie, sorted in ascending order).
  • The replacement must be in-place, do not allocate extra memory.

Here are some examples. Inputs are in the left-hand column and its corresponding outputs are in the right-hand column.

1,2,3 → 1,3,2
3,2,1 → 1,2,3
1,1,5 → 1,5,1

題意:實現一個求下一個排列的方法。要求:1) 下一個排列要在字典序上大於當前排列;2) 如果沒有比當前序列更大的排列,那麼下一個爲最小的排列;3) 需要在原地操作,即不用額外的空間。

分析:按照上面的方法直接實現即可。需要注意與上面方法不同的是,下面的實現是循環的,即字典序的最後一個排列[4,3,2,1]的後面是[1,2,3,4]。

public class Solution {
    public void nextPermutation(int[] num) {
        // find the last adjacent two element that is in ascending order
        int i = num.length - 1;
        while (i > 0 && num[i - 1] >= num[i]) {
            i--;
        }
        
        // if the sequence is already in descending order, reverse the whole sequence
        if (i == 0) {
            reverse(num, 0, num.length - 1);
            return;
        }
        
        // find the last element that is larger than num[i-1]
        int j = num.length - 1;
        while (j >= i && num[i - 1] >= num[j]) {
            j--;
        }
        
        // exchange num[i-1] and num[j]
        int tmp = num[i - 1];
        num[i - 1] = num[j];
        num[j] = tmp;
        
        // reverse the sequence after i-1
        reverse(num, i, num.length - 1);
    }
    
    
    public void reverse(int[] num, int start, int end) {
        int l = start;
        int r = end;
        while (l < r) {
            int tmp = num[l];
            num[l] = num[r];
            num[r] = tmp;
            l++;
            r--;
        }
    }
}
 





Permutations

Description: Given a collection of numbers, return all possible permutations. For example, [1,2,3] have the following permutations: [1,2,3], [1,3,2], [2,1,3], [2,3,1], [3,1,2], and [3,2,1].

題意:求集合中所有數的全排列。

分析:我們知道n個數的全排列有n!個,那麼根據 Next Permutation 一個一個求出即可。

<pre name="code" class="java" style="font-size: 16px; line-height: 28.8px;">public class Solution {
    public List<List<Integer>> permute(int[] num) {
        List<List<Integer>> ans = new ArrayList<List<Integer>>();
 
        int i = 0;
        do {
            // turn every permutation to a list and add it to the answer
            List<Integer> list = new ArrayList<Integer>();
            for (int x : num) {
                list.add(x);
            }
            ans.add(list);
            
            // get next permutation
            nextPermutation(num);
            
        } while (++i < factorial(num.length));
        
        return ans;
    }
    
    
    public void nextPermutation(int[] num) {
        // find the last adjacent two element that is in ascending order
        int i = num.length - 1;
        while (i > 0 && num[i - 1] >= num[i]) {
            i--;
        }
        
        // if the sequence is already in descending order, reverse the whole sequence
        if (i == 0) {
            reverse(num, 0, num.length - 1);
            return;
        }
        
        // find the last element that is larger than num[i-1]
        int j = num.length - 1;
        while (j >= i && num[i - 1] >= num[j]) {
            j--;
        }
        
        // exchange num[i-1] and num[j]
        int tmp = num[i - 1];
        num[i - 1] = num[j];
        num[j] = tmp;
        
        // reverse the sequence after i-1
        reverse(num, i, num.length - 1);
    }
    
    
    public void reverse(int[] num, int start, int end) {
        int l = start;
        int r = end;
        while (l < r) {
            int tmp = num[l];
            num[l] = num[r];
            num[r] = tmp;
            l++;
            r--;
        }
    }
    
    
    public int factorial(int n) {  
        return (n == 1 || n == 0) ? 1 : factorial(n - 1) * n;  
    }  
}


Permutations II

Description: Given a collection of numbers that might contain duplicates, return all possible unique permutations. For example, [1,1,2] have the following unique permutations: [1,1,2], [1,2,1], and [2,1,1].

題意:求集合中所有數的全排列,注意集合中可能存在重複的數

分析:方法與 Permutations 相同,只是有了重複數後,全排列的總數就不足n!個了,我們的方法是先對所有數排序,即從最小的排列開始找,找到最後一個排列時結束。

public class Solution {
    public List<List<Integer>> permuteUnique(int[] num) {
        List<List<Integer>> ans = new ArrayList<List<Integer>>();
        
        // make the permutation strat from ascending order
        Arrays.sort(num);
        
        do {
            // turn every permutation to a list and add it to the answer
            List<Integer> list = new ArrayList<Integer>();
            for (int x : num) {
                list.add(x);
            }
            ans.add(list);
            
        } while (nextPermutation(num));
        
        return ans;
    }
    
    
    public boolean nextPermutation(int[] num) {
        // find the last adjacent two element that is in ascending order
        int i = num.length - 1;
        while (i > 0 && num[i - 1] >= num[i]) {
            i--;
        }
        
        // if all permutations have been found
        if (i == 0) return false;
        
        // find the last element that is larger than num[i-1]
        int j = num.length - 1;
        while (j >= i && num[i - 1] >= num[j]) {
            j--;
        }
        
        // exchange num[i-1] and num[j]
        int tmp = num[i - 1];
        num[i - 1] = num[j];
        num[j] = tmp;
        
        // reverse the sequence after i-1
        reverse(num, i, num.length - 1);
        
        return true;
    }
    
    
    public void reverse(int[] num, int start, int end) {
        int l = start;
        int r = end;
        while (l < r) {
            int tmp = num[l];
            num[l] = num[r];
            num[r] = tmp;
            l++;
            r--;
        }
    }
}



Permutation Sequence

Description: The set [1,2,3,…,n] contains a total of n! unique permutations. By listing and labeling all of the permutations in order, We get the following sequence (ie, for n = 3):

1. “123”
2. “132”
3. “213”
4. “231”
5. “312”
6. “321”

Given n and k, return the kth permutation sequence. Note: Given n will be between 1 and 9 inclusive.

題意:按照字典序法,求n個數的全排列中第k個排列,注意返回排列的形式爲字符串。

分析:從最小的排列開始,根據 Next Permutation 找到第k個排列即可。

public class Solution {
    public String getPermutation(int n, int k) {
        // construct the first sequence
        char[] seq = new char[n];
        for (int i = 0; i < n; i++) {
            seq[i] = (char) ('0' + i + 1);
        }
        
        // get the k-th permutation
        for (int i = 1; i < k; i++) {
            nextPermutation(seq);
        }
        
        return String.valueOf(seq);
    }
    
    public void nextPermutation(char[] seq) {
        // find the last adjacent two element that is in ascending order
        int i = seq.length - 1;
        while (i > 0 && seq[i - 1] >= seq[i]) {
            i--;
        }
        
        // if all permutations have been found
        if (i == 0) return;
        
        // find the last element that is larger than seq[i-1]
        int j = seq.length - 1;
        while (j >= i && seq[i - 1] >= seq[j]) {
            j--;
        }
        
        // exchange seq[i-1] and seq[j]
        char tmp = seq[i - 1];
        seq[i - 1] = seq[j];
        seq[j] = tmp;
        
        // reverse the sequence after i-1
        int l = i;
        int r = seq.length - 1;
        while (l < r) {
            tmp = seq[l];
            seq[l] = seq[r];
            seq[r] = tmp;
            l++;
            r--;
        }
    }
}



發佈了161 篇原創文章 · 獲贊 39 · 訪問量 29萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章