从排列组合到N皇后问题

从排列组合到N皇后问题


排列组合问题求解

  • 问题描述:输入一个字符串,按字典序打印出该字符串中字符的所有排列。例如输入字符串abc,则打印出由字符a,b,c所能排列出来的所有字符串组合abc,acb,bac,bca,cab和cba
  • 求解思路:这个最经典的排列组合问题。解决的思路就是,可以对原始的字符串进项划分,第一个和除去第一个字符后剩下的所有。对于第二部分,然后再使用相同的办法进行排列组合。由此可见,这是一个典型的递归的过程。然后依次把后面的每个字符和第一个进行交换,再进行相同的操作,所以这部分可以使用循环完成。

有了大概的思路后,具体的代码如下:

class Solution:
    def Permutation(self, ss):
        if not ss:
            return []
        if len(ss) == 1:
            return [ss]
        l_ss = list(ss)
        l_ss.sort()  # 按字典的顺序进行排序
        res = []  # 返回的所有组合
        for i in range(len(ss)):  # 循环一次,相当于交换第一个元素一次
            if i > 0 and l_ss[i] == l_ss[i-1]:  # 存在相邻的多个元素重复时
                continue
            temps = self.Permutation(''.join(l_ss[:i]) + ''.join(l_ss[i+1:]))  # 递归除去i的其他组合
            for j in temps:
                res.append(l_ss[i]+j)  # 合并
        return res

排列组合问题变体

排列组合问题求解关于该排列组合的问题的拓展形式有很多,例如下面这种:

  • 问题描述:输入一个字符串,按字典序打印出该字符串中所有字符的排列。例如输入字符串abc,则打印出由字符a,b,c, ab, ac , bc, abc(说明bc和cb算一种组合)
  • 求解思路:这个最经典的排列组合问题的变体,大体思路还是使用递归来实现。和上面不同的是,上面的问题描述的是:例如“abcd”,第2次循环时,将b放到了第一个位置,然后对剩下的所有字符“acd”再进行递归, 因此该轮循环结束后,得到的组合都是以b开头的。但是这个问题就没有要对剩下的所有字符进行递归,而是对当前的基准元素后面的所有进行递归。例如第2次循环时, 只需要对b后面的“cd”进行递归即可。

代码实现如下:

def GroupAll(ss):
    if not ss:
       return []
    if len(ss) == 1:
       return [ss]
    l_ss = list(ss)
    l_ss.sort()
    res = []
    for i in range(len(ss)):
        res.append(l_ss[i])
        if i > 0 and l_ss[i] == l_ss[i-1]:
            continue
        temps = GroupAll(''.join(l_ss[i+1:]))  # 只对i后面的进行递归
        for j in temps:
            res.append(l_ss[i]+j)
            res = list(set(res))  # 去除重复的
            res.sort()
    return res

排列组合问题应用–N皇后问题

  • 问题描述:N后问题。在8x8的国际象棋中摆放8个皇后,要求之间不能相互攻击,即:任意两个不同行/不同列/不同对角线。共有多少种摆放方法
  • 求解思路:由于不同行和不同列,那么只能是每行有且只有一个皇后,每列只有一个。那么可以只用0-7来初始化一个数组,标识第i行的那个皇后的列号。由于0-7每个数字都不一样,那么也就避免了在同行或者同列的条件。此时,N后问题就简化成了排列组合问题。在对该数组进行全排列后, 然后判断是否满足对角线要求即可。

上面的代码是针对字符串的,稍做简单的修改就可以实现对数字的排列组合。见代码:

def group_array(self, array):
    if not array:
        return
    if len(array) == 1:
        return [array]
    res = []
    for i in range(len(array)):
        if i > 0 and array[i] == array[i - 1]:
            continue
        temps = group_array(array[0:i] + array[i+1:])
        for j in temps:
            res.append([array[i]]+j)
    return res

N后问题的最主要的工作就剩下判断是否是在对角线上。这个很简单,给数组设置两个指针i, j,如果i-j==array[i]-array[j]或者j-i==array[i]-array[j],说明在对角线上。

def N_queen(columns):
    res = group_array(columns)  # 全排列
    count = 0
    for column in res:
        check = True  # 判断是否满足条件
        for i in range(len(column)):
            for j in range(i + 1, len(column)):
                if i - j == column[i] - column[j] or i - j == column[j] - column[i]:
                    check = False
        if check:  # 满足条件
            count += 1
    print(len(res))
    return count

执行上述代码,会发现对于N=8的时候,共有40320种组合。其中,满足要求的,即N=8的皇后问题的解共有92个。

你可能感兴趣的:(递归问题)