您的位置:首页 > 其它

算法思维方式—— 由排列组合想到的

2017-08-16 17:41 99 查看
最近算法题也刷了不少,小有感悟。

我觉得刷题时一般的思维方式是迭代思维。就是我们老是想着通过循环,通过顺序解决下一个来迭代解决整个问题。

典型事例有:2Sum, 3Sum, 排序问题,以及用双指针或快慢指针法解决的问题。

迭代思维是一种很直接的思维方式,但绝不简单,因为找到正确的循环方式并不是一件容易的事情。

但有些问题用迭代思维方式是很难解决的,或者说这些问题本身就不适合用循环来解。

比如求组合数问题。C(n, 2)还能用2层循环来解,但C(n, m)呢?用迭代就很难求解了,不自然。这是用递归思维方式却很自然。

先取一个,把问题化为C(n, m-1), 再取一个,把问题化为C(n, m-2), 如此递归即可。

public static int combinationNum = 0;
public static void combine(int[] target, int[] result, int st, int index,int m){
if (m == 0){
combinationNum++;
for (int i : result){
System.out.printf(i + " ");
}
System.out.println();
System.out.println(combinationNum);
}else {
for (int i = index; i < target.length - m + 1; i++) {
result[st] = target[i];
combine(target, result, st+1, i+1,m-1);
}
}
}


  求排列数也是如此,把A(n,n) 化为A(n-1,n-1),直至化为A(1,1)。

public static void swap(int[] result, int st, int ele){
int tmp = result[st];
result[st] = result[ele];
result[ele] = tmp;
}

//轮番把各个元素放在第一位,然后递归求解。注意复位。
public static void arrange(int[] result, int st){
if (st == result.length-1){
for (int i : result){
System.out.printf(i + " ");
}
System.out.println();
}else {
for (int i = st; i < result.length; i++) {
swap(result, i, st);
arrange(result, st+1);
swap(result,i , st);
}
}
}


  对于这类型的问题用递归的感觉就是干净,简洁,有一种逻辑的美感。

不过把问题递归化并不是一件容易的事。有一种常用的技巧是“一子动天下”。就是针对一个元素的有无进行分类讨论,这个元素常常是最后一个。这时往往可以把问题二分递归化。

上面两个例子实际上也是针对一个元素进行讨论,不过它们是把问题多分化,所以外层有循环来遍历。下面给出一个二分递归化的例子。

给出一个数组,里面是不重复的int 数字。 求满足和为S的所有数字组合。(数组
[2, 3, 6, 7],
和为
7
, 结果为:[ [7], [2, 2, 3] ] )。也就是换硬币问题。

这时我们可以针对最后一个元素是否包含,把问题二分化。然后递归遍历整个解法空间:

private List<List<Integer>> result = new ArrayList<>();

//一子动天下的典型事例,递归思维方式的典型。dfs遍历整个解法空间,自然得出结果。干净,简洁。
public void combinations(int[] candidates, int target, List<Integer> combination, int limit){
if (limit < 0 ){
return;
}else if (target == 0){
result.add(combination);
}else {
if (target - candidates[limit] >= 0){
combination.add(candidates[limit]);
combinations(candidates, target - candidates[limit], new ArrayList<>(combination), limit);
combination.remove(combination.size()-1);
}
combinations(candidates, target, combination, limit-1);
}
}

public List<List<Integer>> combinationSum(int[] candidates, int target) {
Arrays.sort(candidates);
int limit = candidates.length-1;
combinations(candidates, target, new ArrayList<Integer>(), limit);
return result;
}


  

算法问题多变,复杂。如果迭代不行,不妨试试递归方式。“一子动天下”是一种较好的讨论方式。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: