【leetcode 排列组合问题】Next Permutation | Permutations | Permutations II | Permutation Sequence

leetcode上关于数字排列组合问题有四个,分别是:

(1)Next Permutation :已知一个数组代表某个排列组合,求出比其代表的数字大的下一个排列组合,通过重组数组中的数字实现

(2)Permutations:已知一组数字,求出所有可以表示出的排列组合

(3)Permutations II:已知一组数字可能包含重复,求出所有可以表示出的排列组合(无重复的)

(4)Permutation Sequence:已知一组数字1~n有n!种排列组合,从小到大排列,返回第k种排列组合

下面针对这四道题分别进行解析:

(1)Next Permutation题目:

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

解析:输入是一个数字表示一个排列组合,例如int [] num = {1,2,3},表示数字123,要求求出相同数字表示的排列组合中下一个大的数字,123的下一个数字为132,则num = {1,3,2}。如果当前排列组合代表的数字已经是最大的数字,则返回排列组合最小的数字。

首先的思路是寻找当前数字和下一个数字的规律。如下图所示:5672431的下一个数字是5673124,其中567三位灰色数字不变,将2和3调换位置后,421倒序成124即可获得结果,

(1)那么2是怎么选中的呢?从最后一位往前一位位遍历数字,当num[i]>num[i-1]时(黄色),表示num[i-1]是要被替换的数字(即数字2)。

(2)然后3是怎么选中的呢?3是4到最后一个数字中大于2的最小数字。


Java AC代码:

public class Solution {

	public void nextPermutation(int[] num) {
		int i = num.length - 1;
		for (; i > 0; i--) {
			if (num[i] > num[i - 1]) {
				int minTemp = i;
				for (int j = i; j < num.length; j++) {
					if (num[j] < num[minTemp] && num[j] > num[i - 1]) {
						minTemp = j;
					}
				}
				
				int temp = num[i - 1];
				num[i - 1] = num[minTemp];
				num[minTemp] = temp;
				if (i < num.length - 1) {
					reverse(num, i);
				}
				break;
			}
		}
		if (i == 0) {
			reverse(num, 0);
		}
	}

	public void reverse(int[] num, int i) {  //倒序,实际上用了插入排序
		int p = i + 1, q;
		for (; p < num.length; p++) {
			int temp = num[p];
			for (q = p - 1; q >= i && temp < num[q]; q--) {
				num[q + 1] = num[q];
			}
			num[q+1] = temp;
		}
	}

}

(2)Permutations 题目:

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].

解析:通过给定一个数组,输出所有数组中数字表示的全排列。本题是很典型的dfs题目,可以通过递归实现,但是需要boolean数组记录所有的数字是否已经在全排列中统计过。《剑指offer》给出了一种新做法:

(1)将list分为两个部分,一部分是第一个数字,另一个部分是第一个数字后的所有数字,接下来要求所有第二部分数字的全排列。

(2)拿第一个数字和后面的数字逐个交换。

Java AC代码:

public class Permutations {

	public List<List<Integer>> permute(int[] num) {
		List<List<Integer>> result = new ArrayList<List<Integer>>();
		if (num == null) {
			return null;
		}
		if (num.length == 0) {
			return result;
		}
		List<Integer> list = new ArrayList<Integer>();
		result = recursion(num, result, list, 0);
		return result;
	}

	public List<List<Integer>> recursion(int[] num, List<List<Integer>> result,
			List<Integer> list, int cur) {
		if (cur == num.length) {
			List<Integer> addList = new ArrayList<Integer>(list);
			result.add(addList);
		}
		for (int i = cur; i < num.length; i++) {
			int temp = num[cur];
			num[cur] = num[i];
			num[i] = temp;
			list.add(num[cur]);
			result = recursion(num,result,list,cur+1);
			list.remove(list.size()-1);
			temp = num[cur];
			num[cur] = num[i];
			num[i] = temp;
		}
		return result;
	}

}

(3)Permutations II :

题目:

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].

解析:输入一组可能包含重复的数字,返回所有排列组合,要求所有结果的排列组合没有重复。本题采用dfs做法,和上一道题相比,因为要避免重复的数字组成的重复的全排列,在添加新的数字到list的时候要判断是否和前一个添加到list的数字相同,因为数组进行过排序,所以可以直接判断num[i]和num[i-1]。当num[i]==num[i-1]且num[i-1]被添加进了全排列过就不能把当前的num[i]添加到全排列中。

Java AC代码:

public class Solution {

	public List<List<Integer>> permuteUnique(int[] num) {
		List<List<Integer>> result = new ArrayList<List<Integer>>();
		if (num == null) {
			return null;
		}
		if (num.length == 0) {
			return result;
		}
		Arrays.sort(num);
		List<Integer> list = new ArrayList<Integer>();
		boolean[] ifUsed = new boolean[num.length];
		Arrays.fill(ifUsed, false);
		result = dfs(num, result, list, ifUsed, 0);
		return result;
	}

	public List<List<Integer>> dfs(int[] num, List<List<Integer>> result,
			List<Integer> list, boolean[] ifUsed, int dep) {
		if (dep == num.length) {
			List<Integer> addList = new ArrayList<Integer>(list);
			result.add(addList);
			return result;
		}
		for (int i = 0; i < num.length; i++) {
			if ((0 != i && num[i - 1] == num[i] && !ifUsed[i - 1]) || ifUsed[i]) {
				continue;
			}
			list.add(num[i]);
			ifUsed[i] = true;
			dep++;
			result = dfs(num, result, list, ifUsed, dep);
			dep--;
			list.remove(list.size() - 1);
			ifUsed[i] = false;
		}
		return result;
	}
}

(4)Permutation Sequence:

题目:

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,n表示排列组合的数字由1~n组成(无重复),k表示第k个排列组合序列。该题的某个方法主要思路重点是:对于n位数,假设第一位确定,后面的数字排列组合情况有 (n-1)! 种,所以可以根据后面排列组合的数目推出第一位是什么数字。例如:

当n=3,k=5时,所有的排列的组合有:123,132,213,231,312,321,所以结果是312。

思路:首先,假设第一位确定,后面两位共有2!=2种情况,所以首位为 (5-1)/(2!)+1= 3 (从小到大第三个未访问过的数字)

确定了3后,问题变成了求数字1和2可以组合成的排列组合的第一个数字,其中k的更新公式如下,不能直接取k=k%factorial[i] (会造成k=0)。

k = k % factorial[i] == 0 ? factorial[i] : k % factorial[i];

Java AC代码:

public class Solution{

	public String getPermutation(int n, int k) {

		int factorial[] = new int[10];
		factorial[0] = 1;
		factorial[1] = 1;
		for (int i = 2; i < factorial.length; i++) {
			factorial[i] = i * factorial[i - 1];
		}

		boolean ifVisited[] = new boolean[n + 1];
		Arrays.fill(ifVisited, false);

		StringBuilder sb = new StringBuilder();
		for (int i = n - 1; i >= 0; i--) {
			int temp = (k - 1) / factorial[i] + 1;
			int j = 0;
			while (temp > 0) {
				if (!ifVisited[++j]) {
					temp--;
				}
			}
			sb.append(j);
			ifVisited[j] = true;
			k = k % factorial[i] == 0 ? factorial[i] : k % factorial[i];
			// k %= factorial[i];
		}
		return sb.toString();
	}
}


  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值