《算法笔记》—— 解决 "排列组合问题" 递归的灵活运用

深知自己的算法特别菜,所以想在算法这方面多下工夫
现在被各种专业课支配着,但每天都会抽出一点时间来学习算法 (^ - ^)

相信大家都了解 * 汉诺塔 * 这个问题,我当时学的是云里雾里的(泪目)……

递归

递归简单点说就是自己调用自己,有规律性的调用。

多说无益,直接正题


排列组合

假如我们我abc这三个字符,我们将它所有的组合都列举出来:


a b c
a c b
b a c
b c a
c a b
c b a


细心的朋友会不会发现这样排列的规律呢?

如果没有看出来,那我们将abcd这四个字符列举出来:


abcd
abdc
acbd
acdb
adcb
adbc
bacd
badc
bcad
bcda
bdca
bdac

cbad
cbda
cabd
cadb
cdab
cdba
dbca
dbac
dcba
dcab
dacb
dabc


我们会发现所有的排列组合被分成了四块(这不是故意为之),很明显这样的排列组合是有一定的规律进行排列的。

这四块组合的顺序就是执照 abcd这四个字符进行排序的。


递归思想

我们来分析一下上面的abcd的排列组合,来找出其中的递归思想

  1. 首先,我们发现排列组合以abcd分为四大块。
  2. 其次,我们以a开头的组合情况解析:

abcd
abdc
acbd
acdb
adcb
adbc


我们会发现以a为开头,b,c,d为结尾的组合又分成了三组,它们分别是b,c,d为开头的组合:


bcd
bdc
cbd
cdb

dcb
dbc


而其中,以b为开头的组合,又分成了两组:


cd
dc


而到这里,我们已经不能再分了……

a开头的组合如此,那以bcd开头的那三个组合也是如何。

到这里我想大家应该有了大概的思路了吧
没有也没有关系,因为我们下面来进行代码讲解。


代码详解部分

#include 
using namespace std;

// ch 字符串	k 第一个元素下标		m 最后一个元素的下标
void  permutation(char* ch, int k, int m)
{
    if(k == m)	// 当我们查找到最后时,则输出
    {
        for(int i = 0; i < m; ++i)
        {
            cout << ch[i];
        }
        cout << endl;
    }
    else
    {    
        permutation(ch, k + 1, m);	// 排列出 a开头的所有组合
    
        swap(ch[0], ch[1]);		// 将a与b交换
        permutation(ch, k + 1, m);	// 排列出 b开头的所有组合
        swap(ch[0], ch[1]);		// 再次交换回来

        swap(ch[0], ch[2];		// 将a与c交换
        permutation(ch, k + 1, m);	// 排列出 c开头的所有组合 
        swap(ch[0], ch[1]);		// 再次交换回来		    
    }
}

int main()
{
    char ch[] = { "abc" };
    
    return 0;
}

我们将这个部分的代码进行整合:
《算法笔记》—— 解决
变换成:

for(int i = k; i < m; ++i)
{
    swap(ch[k], ch[i]);	// 交换位置
    permutation(ch, k + 1, m);	// 开始递归
    swap(ch[k], ch[i]);	// 变回来
}

代码解析图:

《算法笔记》—— 解决


作者:浪子花梦
Time:2020.2.19
我要向梦一样自由

你可能感兴趣的:(算法笔记)